KG_Full_Emoticons

Show all the emoticons

// ==UserScript==
// @name         KG_Full_Emoticons
// @namespace    http://klavogonki.ru/
// @version      0.7
// @description  Show all the emoticons
// @author       Patcher
// @match        *://klavogonki.ru/g*
// @match        *://klavogonki.ru/forum/*
// @match        *://klavogonki.ru/u/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=klavogonki.ru
// @grant        none
// ==/UserScript==

const categories = {
  "Boys": [
    "smile", "biggrin", "angry", "blink", "blush", "cool", "dry", "excl", "happy",
    "huh", "laugh", "mellow", "ohmy", "ph34r", "rolleyes", "sad", "sleep", "tongue",
    "unsure", "wacko", "wink", "wub", "first", "second", "third", "power", "badcomp",
    "complaugh", "crazy", "boredom", "cry", "bye", "dance", "gamer", "rofl", "beer",
    "kidtruck", "angry2", "spiteful", "sorry", "boykiss", "kissed", "yes", "no", "heart",
    "hi", "ok", "facepalm", "friends", "shok", "megashok", "dash", "music", "acute", "victory",
    "scare", "clapping", "whistle", "popcorn", "hello", "rose", "good", "silence", "bad", "tea",
    "sick", "confuse", "rofl2", "nervous", "chaingun", "diablo", "cult", "russian", "birthday",
    "champ2", "champ", "confetti", "formula1"
  ],
  "Girls": [
    "girlnotebook", "girlkiss", "curtsey", "girlblum", "girlcrazy", "girlcry",
    "girlwink", "girlwacko", "umbrage", "girlinlove", "girldevil", "girlimpossible",
    "girlwitch", "hysteric", "tender", "spruceup", "girlsad", "girlscare", "girltea",
    "girlsick", "grose", "cheerful", "cheerleader", "girlconfuse", "spruceup1",
    "angrygirl", "clapgirl", "goody", "hiya", "girlsilence", "girlstop", "girlnervous",
    "girlwonder", "girlwonder", "kgrace", "kgagainstaz", "girlkissboy", "girlmusic"
  ],
  "Christmas": [
    "cheers", "christmasevil", "heyfrombag", "merrychristmas", "moose", "santa",
    "santa2", "santa3", "santasnegurka", "snegurka", "snegurochka", "snowball",
    "snowgirlwave", "snowhand", "snowhit", "snowman", "spruce"
  ],
  "Inlove": [
    "adultery", "airkiss", "cave", "flowers", "flowers2", "frog", "girlfrog",
    "girlheart2", "girllove", "grose", "heart2", "heartcake", "hug", "inlove",
    "nolove", "smell", "wecheers", "wedance", "wedding", "wine"
  ],
  "Army": [
    "ak47", "armyfriends", "armyscare", "armystar", "armytongue", "barret",
    "bayanist", "budenov", "captain", "comandos", "fly", "foolrifle", "girlpogran",
    "girlranker", "girlrogatka", "girlvdv", "kirpich", "partizan", "pogran",
    "pogranflowers", "pogranmail", "pogranmama", "pogranminigun", "pogranrose",
    "pograntort", "prival", "radistka", "ranker", "rogatka", "soldier", "tank",
    "uzi", "vdv", "vpered", "vtik"
  ],
  "WomenDay": [
    "boystroking", "cheerleader", "confetti", "enjoygift", "firework", "girlicecream",
    "girlmad", "girlobserve", "girlrevolve", "girlshighfive", "girlstroking", "girlsuper",
    "grats", "hairdryer", "leisure", "primp", "respect", "serenade", "spruceup"
  ],
  "Halloween": [
    "alien", "batman", "bebebe", "bite", "carpet", "clown", "corsair", "cowboy",
    "cyborg", "dandy", "death", "dwarf", "gangster", "ghost", "girlpirate", "holmes",
    "indigenous", "jester", "mafia", "musketeer", "paladin", "pioneer", "pirate",
    "pirates", "robot", "rocker", "spider", "supergirl", "terminator", "turtle",
    "vampire", "witch", "wizard"
  ]
};

// Define emoji symbols for categories
const categoryEmojis = {
  "Boys": "😃",
  "Girls": "👧",
  "Christmas": "🎄",
  "Inlove": "❤️",
  "Army": "🔫",
  "WomenDay": "🌼",
  "Halloween": "🎃",
  "Favourites": "🌟"
};

let roomField = null;
let isEventListenersInitialized = false;
let isPopupCreated = false;
let activeCategory = null;

// Define an array to store the history of active categories
const categoryHistory = [];

// Keep track of the last focused textarea
let lastFocusedTextarea = null;

// Flags to prevent multiple detections of Profile textareas
let hasDetectedProfileTextarea = false;

// Function to load favorite emoticons from localStorage
function loadFavoriteEmoticons() {
  // Retrieve favorite emoticons from localStorage or initialize an empty array if not found
  const favoriteEmoticons = JSON.parse(localStorage.getItem('favoriteEmoticons')) || [];

  // Assign the favorite emoticons to the "Favourites" category using dot notation
  categories.Favourites = favoriteEmoticons;
}

// Example usage
loadFavoriteEmoticons();
// Function to debounce resetting the flags
function debounceResetFlags() {
  setTimeout(() => {
    hasDetectedProfileTextarea = false;
  }, 3000);
}

// Function to handle focus on textareas only for forum
function handleTextareaFocus(event) {
  lastFocusedTextarea = event.target;
}

// Function to determine which chat room we are in
function determineChatRoom() {
  // Check if the roomField has not been set yet
  if (!roomField) {
    // Get the current URL of the webpage
    const currentURL = window.location.href;

    // Define an array of allowed domains
    const allowedDomains = ["klavogonki.ru"];

    // Check if the current URL includes any allowed domains
    if (allowedDomains.some(domain => currentURL.includes(domain))) {
      // Check the URL path to determine the context
      if (currentURL.includes("/gamelist")) {
        // Set roomField to the General chat input field
        roomField = document.querySelector('#chat-general input.text');
        // console.log("Chat Field (General):", roomField);
      } else if (currentURL.includes("/g/?gmid=")) {
        // Set roomField to the Game chat input field
        roomField = document.querySelector('div[id*="chat-game"] input.text');
        // console.log("Chat Field (Game):", roomField);
      } else if (currentURL.includes("/forum")) {
        // Select all forum textareas
        let allForumTextareas = document.querySelectorAll('textarea');
        // Set roomField to an array of Forum textareas
        roomField = allForumTextareas;
        console.log("Chat Field (Forum):", roomField);
        allForumTextareas.forEach(textArea => {
          textArea.addEventListener('focus', handleTextareaFocus);
        });
      } else if (currentURL.includes("/u/")) {
        // Check if neither Direct Message, Saved Message, nor Journal has been detected yet
        if (!hasDetectedProfileTextarea) {
          // Create a Mutation Observer to watch for changes in the document
          const observer = new MutationObserver(() => {
            // Check for the presence of Direct Message, Saved Message, Edit Bio and Journal textareas
            const directMessageTextarea = document.querySelector('.dlg-send-user-message .message-text textarea');
            const savedMessageTextarea = document.querySelector('.profile-messages .dialog-write textarea');
            const editBioTextarea = document.querySelector('.profile-edit-bio .edit textarea');
            const journalMessageTextarea = document.querySelectorAll('.profile-root .journal .write textarea');

            // Find the focused textarea among the detected textareas
            for (const textarea of journalMessageTextarea) {
              if (document.activeElement === textarea) {
                // Store in the global variable
                lastFocusedTextarea = textarea;
                break;
              }
            }

            // Set roomField based on the detected textarea
            if (directMessageTextarea && !hasDetectedProfileTextarea) {
              // Set roomField to the Direct Message textarea
              roomField = directMessageTextarea;
              console.log("Chat Field (Direct Message):", roomField);
            } else if (savedMessageTextarea && !hasDetectedProfileTextarea) {
              // Set roomField to the Saved Message textarea
              roomField = savedMessageTextarea;
              console.log("Chat Field (Saved Message):", roomField);
            } else if (lastFocusedTextarea && !hasDetectedProfileTextarea) {
              // Set roomField to the last focused textarea
              roomField = lastFocusedTextarea;
              console.log("Chat Field (Journal):", roomField);
            } else if (editBioTextarea && !hasDetectedProfileTextarea) {
              // Set roomField to the Edit Bio Textarea
              roomField = editBioTextarea;
              console.log("Chat Field (Edit Bio):", roomField);
            }

            // Initialize event listeners for this textarea
            initializeEventListeners();
            // Set the flag to prevent further detections
            hasDetectedProfileTextarea = true;
            // Debounce resetting the flags
            debounceResetFlags();
          });

          // Start observing mutations in the document's structure
          observer.observe(document.documentElement, { childList: true, subtree: true });
        }
      }
    }
  }
  // Return the determined or detected roomField
  return roomField;
}

// Function to remove the emoticons popup
function removeEmoticonsPopup() {
  const popupBox = document.querySelector('.emoticons-popup');
  if (popupBox) {
    document.body.removeChild(popupBox);

    // Remove event listener for the "Tab" key press
    document.removeEventListener('keydown', changeCategoryOnTabPress);

    isPopupCreated = false; // Reset the flag when the popup is removed
  }
}

// Function to calculate the maximum image width and height for a given category
function calculateMaxImageDimensions(category) {
  const maxImageWidth = 34;
  const maxImageHeight = 34;

  const maxImageWidthCalculated = category.reduce((maxWidth, emoticon) => {
    const emoticonName = emoticon;
    const imgSrc = `/img/smilies/${emoticonName}.gif`;
    const img = new Image();
    img.src = imgSrc;
    const imageWidth = img.width;
    return Math.max(maxWidth, imageWidth);
  }, maxImageWidth);

  return { maxImageWidth: maxImageWidthCalculated, maxImageHeight };
}

// Function to initialize event listeners
function initializeEventListeners() {
  if (!isEventListenersInitialized) {
    // Load favorite emoticons from localStorage
    loadFavoriteEmoticons();

    // Find the "Favourites" button within the category buttons
    const favouritesButton = document.querySelector('.category-buttons button[data-category="Favourites"]');

    // Check if the "Favourites" button exists
    if (favouritesButton) {
      // Add a click event listener to the "Favourites" button
      favouritesButton.addEventListener('click', function (event) {
        // Check if the shift key is pressed while clicking the button
        if (event.shiftKey) {
          // Check if there is a category history
          if (categoryHistory.length > 0) {
            // Pop the previous active category from the history
            const previousCategory = categoryHistory.pop();

            // Set the active category to the previous one
            changeActiveCategoryOnClick(previousCategory);

            // Clear all favorites from localStorage
            localStorage.removeItem('favoriteEmoticons');

            // Update the state of category buttons
            updateCategoryButtonsState(activeCategory);

            // Update the emoticons container
            updateEmoticonsContainer();
          }
        }
      });
    }

    document.addEventListener('keydown', function (event) {
      if (event.ctrlKey && event.code === 'Semicolon') {
        event.preventDefault();
        toggleEmoticonsPopup(); // Toggle the emoticons panel
        // console.log('Ctrl + Semicolon pressed');
      } else if (event.key === 'Escape') {
        removeEmoticonsPopup(); // Close the emoticons panel if it exists
        console.log('Escape key pressed');
      }
    });

    // Attach a mousedown event listener to the document and use event delegation
    document.addEventListener('mousedown', function (event) {
      // Check if the target element matches any of the textarea selectors
      const textareaSelectors = [
        '.dlg-send-user-message .message-text textarea',
        '.profile-messages .dialog-write textarea',
        '.profile-edit-bio .edit textarea',
        '.profile-root .journal .write textarea'
      ];

      if (textareaSelectors.some(selector => event.target.matches(selector))) {
        if (event.shiftKey && event.detail === 2) {
          event.preventDefault(); // Prevent the default behavior of double-click
          toggleEmoticonsPopup();
          console.log('Textarea Double-Click Event Executed');
        }
      }
    });

    // Attempt to find the chat input field
    roomField = determineChatRoom();

    // Check if the chat input field was found
    if (roomField) {
      // Attach a mousedown event listener to the chat field(s)
      if (roomField instanceof NodeList || roomField instanceof HTMLCollection) {
        roomField.forEach(textArea => {
          textArea.addEventListener('mousedown', function (event) {
            if (event.shiftKey && event.detail === 2) {
              event.preventDefault(); // Prevent the default behavior of double-click
              toggleEmoticonsPopup();
            }
          });
        });
      } else {
        roomField.addEventListener('mousedown', function (event) {
          if (event.shiftKey && event.detail === 2) {
            event.preventDefault(); // Prevent the default behavior of double-click
            toggleEmoticonsPopup();
          }
        });
      }
    }

    isEventListenersInitialized = true;
  }
}

// Toggles the emoticons panel by checking its existence and taking appropriate action.
function toggleEmoticonsPopup() {
  // Find the emoticons panel element with the class '.emoticons-popup'
  const emoticonsPopup = document.querySelector('.emoticons-popup');

  // Check if the emoticons panel element exists in the document
  if (emoticonsPopup) {
    // If the panel exists, close it by removing it from the DOM
    removeEmoticonsPopup();
  } else {
    // If the panel doesn't exist, create and display it with the active category
    createEmoticonsPopup(activeCategory);
  }
}

// Helper function to check if an element matches a selector
function matchesSelector(element, selector) {
  return element && element.matches && element.matches(selector);
}

// Get current body background color
const bodyBackgroundColor = window.getComputedStyle(document.body).backgroundColor;
// Store the lightness with the helper function getLightness
const bodyLightness = getLightness(bodyBackgroundColor);

// Function to get lightness from an RGB color string and round it
function getLightness(color) {
  const match = color.match(/\d+/g);
  if (match && match.length === 3) {
    const r = parseInt(match[0]);
    const g = parseInt(match[1]);
    const b = parseInt(match[2]);
    const max = Math.max(r, g, b) / 255;
    const min = Math.min(r, g, b) / 255;
    const lightness = Math.round(((max + min) / 2) * 100); // Round to the nearest integer
    return lightness;
  }
  return 0;
}

// Function to get the adjusted background color in HSL format
function getAdjustedBackground(caseType) {
  let adjustment = 0;

  switch (caseType) {
    case 'popupBackground':
      adjustment = 10;
      break;
    case 'defaultButton':
      adjustment = 15;
      break;
    case 'hoverButton':
      adjustment = 25;
      break;
    case 'activeButton':
      adjustment = 35;
      break;
    default:
      adjustment = 0; // Default case, no adjustment
  }

  const adjustedLightness = bodyLightness < 50 ? bodyLightness + adjustment : bodyLightness - adjustment;
  const adjustedBackgroundColor = `hsl(0, 0%, ${adjustedLightness}%)`;
  return adjustedBackgroundColor;
}

// Store adjusted backgrounds for elements
const popupBackground = getAdjustedBackground('popupBackground');
const defaultButtonBackground = getAdjustedBackground('defaultButton');
const hoverButtonBackground = getAdjustedBackground('hoverButton');
const activeButtonBackground = getAdjustedBackground('activeButton');

// Function to create the emoticons popup for a given category
function createEmoticonsPopup(category) {
  // POPUP ITSELF
  if (!isPopupCreated) {
    const popupBox = document.createElement('div');

    if (popupBox) {
      // Add event listener for the "double-click"
      popupBox.addEventListener('dblclick', removeEmoticonsPopup);

      // Add event listener for the "Tab" key press
      document.addEventListener('keydown', changeCategoryOnTabPress);
    }

    // CLOSE BUTTON
    // Create a close button
    const closeButton = document.createElement('button');
    closeButton.innerHTML = '&#x2716;'; // Unicode character (X)
    closeButton.style.backgroundColor = 'rgb(57, 19, 19)';
    closeButton.style.color = 'rgb(217, 140, 140)';
    closeButton.style.border = '1px solid rgb(153, 51, 51)';
    closeButton.style.outline = 'none';
    closeButton.style.cursor = 'pointer';
    closeButton.style.width = '50px';
    closeButton.style.height = '50px';
    closeButton.style.margin = '8px';
    closeButton.style.position = 'absolute';
    closeButton.style.right = '0';

    // Add a click event listener to the close button
    closeButton.addEventListener('click', function () {
      removeEmoticonsPopup();
    });

    // Append the close button to the popupBox
    popupBox.appendChild(closeButton);

    popupBox.style.position = 'fixed';
    popupBox.style.display = 'grid';
    popupBox.style.gridTemplateRows = "50px auto";
    popupBox.style.gridGap = '10px';
    popupBox.style.backgroundColor = popupBackground;
    popupBox.style.border = 'none';
    popupBox.style.padding = '10px';
    popupBox.style.zIndex = '9999';
    popupBox.style.top = '50%';
    popupBox.style.left = '50%';
    popupBox.style.transform = 'translate(-50%, 150px)';
    popupBox.style.maxWidth = '50vw';
    popupBox.style.minWidth = '550px';
    popupBox.style.width = '50vw';
    popupBox.style.maxHeight = '50vh';
    popupBox.style.overflow = 'auto';
    popupBox.style.top = '0';

    // Create and append category buttons
    popupBox.appendChild(createCategoryContainer(activeCategory));

    // Create the emoticons container and add inside it emoticon buttons
    createEmoticonsContainer(category)
      .then(emoticonsContainer => {
        popupBox.appendChild(emoticonsContainer);
      });

    popupBox.classList.add('emoticons-popup');
    document.body.appendChild(popupBox);
    isPopupCreated = true;
  }
}

// Function to create category buttons for the given category
function createCategoryContainer(category) {
  const categoryButtonsContainer = document.createElement('div');
  categoryButtonsContainer.classList.add('category-buttons');
  categoryButtonsContainer.style.display = 'flex';
  categoryButtonsContainer.style.justifyContent = 'center';

  function addCategoryButtonListeners(categoryButton, categoryKey) {
    // Add a click event listener to the category buttons
    categoryButton.addEventListener('click', (event) => {
      if (!event.shiftKey && !event.ctrlKey) {
        // Call a function to change the active category only if neither Shift nor Ctrl key is pressed
        changeActiveCategoryOnClick(categoryKey);
      }
    });

    categoryButton.addEventListener('mouseover', () => {
      categoryButton.style.backgroundColor = hoverButtonBackground;
    });

    categoryButton.addEventListener('mouseout', () => {
      // Update the background color based on whether it's the active category or not
      categoryButton.style.backgroundColor = categoryKey === activeCategory ? activeButtonBackground : defaultButtonBackground;
    });
  }

  // Function to handle the click event for the "Favourites" button
  function handleFavouritesButtonClick(event) {
    if (event.shiftKey) {
      // Clear all favorites
      localStorage.removeItem('favoriteEmoticons');
      categories.Favourites = [];
      // Check if there's a category in the categoryHistory array
      if (categoryHistory.length > 0) {
        // Get the previously active category from the history
        const previousCategory = categoryHistory.pop();
        // Save the new active category to localStorage
        localStorage.setItem('activeCategory', previousCategory);
        // Update the activeCategory variable
        activeCategory = previousCategory;
      }
      // Update the state of category buttons
      updateCategoryButtonsState(activeCategory);
      // Update the emoticons container
      updateEmoticonsContainer();
    }
  }

  for (const categoryKey in categories) {
    if (categories.hasOwnProperty(categoryKey)) {
      const categoryButton = document.createElement('button');
      categoryButton.innerHTML = categoryEmojis[categoryKey]; // Use emoji symbol
      categoryButton.dataset.category = categoryKey; // Set a dataset attribute for identifying the category

      const savedCategory = localStorage.getItem('activeCategory');
      const isButtonActive = savedCategory === categoryKey;

      // Determine the button background color based on whether it's the active category or not
      const categoryButtonBackground = isButtonActive ? activeButtonBackground : defaultButtonBackground;

      categoryButton.style.backgroundColor = categoryButtonBackground;
      categoryButton.style.border = 'none';
      categoryButton.style.outline = 'none';
      categoryButton.style.marginRight = '5px';
      categoryButton.style.cursor = 'pointer';
      categoryButton.style.minWidth = '50px';
      categoryButton.style.minHeight = '50px';
      categoryButton.style.fontSize = '1.4em';

      if (categoryKey === 'Favourites') {
        setFavouritesButtonOpacity(categoryButton); // Use the function to control opacity

        // Attach the click event listener to the "Favourites" button
        categoryButton.addEventListener('click', handleFavouritesButtonClick);
      }

      // Add event listeners for the category button using the function
      addCategoryButtonListeners(categoryButton, categoryKey);

      categoryButtonsContainer.appendChild(categoryButton);
    }
  }

  return categoryButtonsContainer;
}

// Function to update the state of category buttons
function updateCategoryButtonsState(newCategory) {
  const categoryButtons = document.querySelectorAll('.category-buttons button');

  categoryButtons.forEach(button => {
    const categoryKey = button.dataset.category;
    const isButtonActive = categoryKey === newCategory;
    const categoryButtonBackground = isButtonActive ? activeButtonBackground : defaultButtonBackground;

    button.style.backgroundColor = categoryButtonBackground;

    if (categoryKey === 'Favourites') {
      setFavouritesButtonOpacity(button); // Control the opacity of the "Favourites" button
    }
  });
}

// Function to control the opacity of the "Favourites" button based on its content
function setFavouritesButtonOpacity(categoryButton) {
  if (categories.Favourites.length === 0) {
    categoryButton.style.opacity = 0.5; // Set opacity to 0.5 if "Favourites" is empty
  } else {
    categoryButton.style.removeProperty('opacity'); // Remove opacity if "Favourites" is not empty
  }
}

// Function to create the emoticons container with buttons for a given category
function createEmoticonsContainer(category) {
  // Define the isEmoticonFavorite function
  function isEmoticonFavorite(emoticon) {
    const favoriteEmoticons = JSON.parse(localStorage.getItem('favoriteEmoticons')) || [];
    return favoriteEmoticons.includes(emoticon);
  }

  const emoticonButtonsContainer = document.createElement('div');
  emoticonButtonsContainer.classList.add('emoticon-buttons');
  emoticonButtonsContainer.style.display = 'none'; // Initially hide the container
  emoticonButtonsContainer.style.gridGap = '10px';

  const imageLoadPromises = [];

  categories[category].forEach(emoticon => {
    const emoticonButton = document.createElement('button');
    const emoticonName = emoticon;
    const imgSrc = `/img/smilies/${emoticonName}.gif`;
    const imgAlt = emoticonName;
    const buttonTitle = emoticonName;

    // Check if the emoticon is a favorite and set the background color accordingly
    if (category !== 'Favourites') {
      emoticonButton.style.backgroundColor = isEmoticonFavorite(emoticon) ? activeButtonBackground : defaultButtonBackground;
    } else {
      emoticonButton.style.backgroundColor = defaultButtonBackground; // For the "Favourites" category, always use the default background
    }

    emoticonButton.innerHTML = `<img src="${imgSrc}" alt="${imgAlt}">`;
    emoticonButton.title = buttonTitle;
    emoticonButton.style.border = 'none';
    emoticonButton.style.outline = 'none';

    const imageLoadPromise = new Promise(resolve => {
      const img = new Image();
      img.onload = () => {
        resolve();
      };
      img.src = imgSrc;
    });

    imageLoadPromises.push(imageLoadPromise);

    emoticonButton.addEventListener('click', function (event) {
      if (event.ctrlKey) {
        // If Ctrl key is pressed, just insert the emoticon code
        insertEmoticonCode(emoticon);
      } else if (event.shiftKey && activeCategory === 'Favourites') {
        // If Shift key is pressed and in the "Favourites" category, remove the emoticon from "Favourites" if it exists
        const favoriteEmoticons = JSON.parse(localStorage.getItem('favoriteEmoticons')) || [];
        const index = favoriteEmoticons.indexOf(emoticon);
        if (index !== -1) {
          favoriteEmoticons.splice(index, 1);
          localStorage.setItem('favoriteEmoticons', JSON.stringify(favoriteEmoticons));
          // Update the categories object
          const favIndex = categories.Favourites.indexOf(emoticon);
          if (favIndex !== -1) {
            categories.Favourites.splice(favIndex, 1);
          }
          // Update the state of category buttons
          updateCategoryButtonsState(activeCategory);
          // Update the emoticons container
          updateEmoticonsContainer();
        }
      } else if (event.shiftKey && activeCategory !== 'Favourites') {
        // If Shift key is pressed and not in the "Favourites" category, add the emoticon to "Favourites"
        const favoriteEmoticons = JSON.parse(localStorage.getItem('favoriteEmoticons')) || [];
        if (!favoriteEmoticons.includes(emoticon)) {
          favoriteEmoticons.push(emoticon);
          localStorage.setItem('favoriteEmoticons', JSON.stringify(favoriteEmoticons));
          categories.Favourites.push(emoticon);
          // Update the state of category buttons for "Favourites"
          updateCategoryButtonsState(activeCategory);
        }
      } else {
        // If neither Ctrl nor Shift is pressed, insert the emoticon code and remove the emoticons popup
        insertEmoticonCode(emoticon);
        removeEmoticonsPopup();
      }
    });

    emoticonButton.addEventListener('mouseover', () => {
      emoticonButton.style.backgroundColor = hoverButtonBackground;
    });

    emoticonButton.addEventListener('mouseout', () => {
      // Check if the emoticon is a favorite and set the background color accordingly
      if (category !== 'Favourites') {
        emoticonButton.style.backgroundColor = isEmoticonFavorite(emoticon) ? activeButtonBackground : defaultButtonBackground;
      } else {
        // For the "Favourites" category, always use the default background
        emoticonButton.style.backgroundColor = defaultButtonBackground;
      }
    });

    emoticonButtonsContainer.appendChild(emoticonButton);
  });

  // Wait for all images to load before updating grid properties and making it visible
  return Promise.all(imageLoadPromises).then(() => {
    // Calculate the maximum image width and height again after all images are loaded
    const { maxImageWidth: newMaxImageWidth, maxImageHeight: newMaxImageHeight } = calculateMaxImageDimensions(categories[category]);

    // Update grid properties with new values
    emoticonButtonsContainer.style.gridTemplateColumns = `repeat(auto-fit, minmax(${newMaxImageWidth}px, 1fr))`;
    emoticonButtonsContainer.style.gridAutoRows = `minmax(${newMaxImageHeight}px, auto)`;

    // Make it visible after all images are loaded
    emoticonButtonsContainer.style.display = 'grid';

    return emoticonButtonsContainer;
  });
}

// Function to update the emoticons container based on the active category
function updateEmoticonsContainer() {
  // Remove the existing emoticons container (if any)
  const existingEmoticonsContainer = document.querySelector('.emoticon-buttons');
  if (existingEmoticonsContainer) {
    existingEmoticonsContainer.remove();
  }

  // Create a new emoticons container with the updated category
  createEmoticonsContainer(activeCategory)
    .then(emoticonsContainer => {
      // Append the new container to the popup (if the popup exists)
      const existingPopup = document.querySelector('.emoticons-popup');
      if (existingPopup) {
        // Append the new category buttons container
        existingPopup.appendChild(emoticonsContainer);
      }
    });
}

// Function to insert emoticon code into the input field
function insertEmoticonCode(emoticon) {
  // Get the input or textarea element (roomField)
  const roomField = lastFocusedTextarea || determineChatRoom();

  // Check if roomField is not a valid input or textarea element
  if (!roomField || !(roomField instanceof HTMLInputElement) && !(roomField instanceof HTMLTextAreaElement)) {
    console.log('roomField is not in focus. Please click on the input field to select it before inserting the emoticon code.');
    return; // Exit the function early
  }

  // Get the cursor position
  const cursorPosition = roomField.selectionStart || 0; // Default to 0 if selectionStart is not supported

  // Check if the current URL indicates a forum page
  const isForumPage = window.location.href.includes("/forum");

  // Set the format based on whether it's a forum or not
  let emoticonCode;
  if (isForumPage) {
    // Forum BBCode format
    emoticonCode = `[img]https://klavogonki.ru/img/smilies/${emoticon}.gif[/img] `;
  } else {
    // Chat format
    emoticonCode = `:${emoticon}: `;
  }

  // Get the current value of the input field
  const currentValue = roomField.value || '';

  // Insert the emoticon code at the cursor position
  const newValue = currentValue.slice(0, cursorPosition) + emoticonCode + currentValue.slice(cursorPosition);

  // Update the input field value with the new value
  roomField.value = newValue;

  // Set the cursor position after the inserted emoticon
  roomField.setSelectionRange(cursorPosition + emoticonCode.length, cursorPosition + emoticonCode.length);

  // Focus on the input field
  roomField.focus();
}

// Function to change the active category
function changeActiveCategoryOnClick(newCategory) {
  // Check if the new category is empty and prevent switching if it is
  if (isCategoryEmpty(newCategory)) {
    return;
  }

  // Store the current active category in the history array if it's not "Favourites"
  if (activeCategory !== 'Favourites') {
    categoryHistory.push(activeCategory);
  }

  // Save the new active category to localStorage
  localStorage.setItem('activeCategory', newCategory);

  // Update the activeCategory variable
  activeCategory = newCategory;

  // Update the state of category buttons
  updateCategoryButtonsState(activeCategory);

  // Call the function to update the emoticons container
  updateEmoticonsContainer();
}

const categoryKeys = Object.keys(categories); // Define categoryKeys

// Function to change the active category based on the "Tab" key press
function changeCategoryOnTabPress(event) {
  const emoticonsPopup = document.querySelector('.emoticons-popup');

  if (event.key === 'Tab' && emoticonsPopup !== null) {
    event.preventDefault();

    // Get the currently active category from localStorage
    const activeCategory = localStorage.getItem('activeCategory');

    // Find the index of the active category in categoryKeys
    const activeCategoryIndex = categoryKeys.indexOf(activeCategory);

    // Determine the index of the next category to focus, cycling if necessary
    let nextCategoryIndex = (activeCategoryIndex + 1) % categoryKeys.length;

    // Check if "Favourites" is empty
    const favoriteEmoticons = JSON.parse(localStorage.getItem('favoriteEmoticons')) || [];
    if (nextCategoryIndex === categoryKeys.indexOf('Favourites') && favoriteEmoticons.length === 0) {
      // "Favourites" is empty, so start from the first category
      nextCategoryIndex = 0;
    }

    // Get the name of the next category
    const nextCategory = categoryKeys[nextCategoryIndex];

    // Update the active category in localStorage
    localStorage.setItem('activeCategory', nextCategory);

    // Optionally, you can call changeActiveCategory to handle the category change
    changeActiveCategoryOnClick(nextCategory);

    // Log the active category for debugging
    // console.log(`Active Category: ${nextCategory}`);
  }
}

// Initialize event listeners and create the emoticons popup with the default category
initializeEventListeners();

// Retrieve the active category from localStorage or use the default category if not found
activeCategory = localStorage.getItem('activeCategory') || 'Boys';
function isCategoryEmpty(category) {
  if (category === 'Favourites') {
    const favoriteEmoticons = JSON.parse(localStorage.getItem('favoriteEmoticons')) || [];
    return favoriteEmoticons.length === 0;
  }
  // Handle other categories here if needed
  return false;
}