Input Text Logger

Track continuous text input on websites and save to Tampermonkey's local storage

// ==UserScript==
// @name         Input Text Logger
// @namespace    https://github.com/putsan
// @author       @putsan.bsky.social
// @version      0.1.2
// @description  Track continuous text input on websites and save to Tampermonkey's local storage
// @icon         https://github.com/putsan/Tampermonkey_scripts/blob/1b111563ad358762c1611a7e1c48544cd4fcf833/resources/icons/image_2023-12-27_22-31-58.png?raw=true
// @include      *
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function () {
  "use strict";

    // --- Variables Initialization ---
  let currentText = "";
  let lastDomain = "";
  let lastTimestamp = "";
  let inactivityTimer;

// --- Styles for the Popup ---
  const popupStyle = `
      position: fixed;
      bottom: 100px;
      right: 10px;

      display: none;
      flex-direction: column;
      gap: 10px;
      width: 200px;
      padding: 10px;

      background: white;
      border: 1px solid black;
      z-idex: 999;
  `;

  // --- External CSS Library Link (Pure.css) ---
const pureCssLink = document.createElement('link');
pureCssLink.href = 'https://cdn.jsdelivr.net/npm/purecss@3.0.0/build/pure-min.css';
pureCssLink.rel = 'stylesheet';
pureCssLink.type = 'text/css';

// Додавання створеного тегу до секції head веб-сторінки
document.head.appendChild(pureCssLink);


  // --- Function to Save Data ---
  /**
   * Saves text, domain, and timestamp to Tampermonkey's local storage.
   * @param {string} newText - The new text to save.
   * @param {string} newDomain - The domain from which the text was captured.
   * @param {string} newTimestamp - The timestamp when the text was saved.
   */
  function saveData(newText, newDomain, newTimestamp) {
    // Отримання існуючих даних
    let existingData = GM_getValue("savedText", []);
    console.log(23, "до", GM_getValue("savedText"));

    // Додавання нових даних
    existingData.unshift({
      text: newText,
      domain: newDomain,
      timestamp: newTimestamp,
    });

    // Збереження оновлених даних
    GM_setValue("savedText", existingData);
    console.log(31, "після", GM_getValue("savedText"));
  }

  // --- Inactivity Timer Reset Function ---
  function resetInactivityTimer() {
    clearTimeout(inactivityTimer);
    inactivityTimer = setTimeout(() => {
      if (currentText.length > 0) {
        saveData(currentText, lastDomain, lastTimestamp);
        currentText = "";
      }
    }, 10000); // 10 секунд неактивності
  }

  document.addEventListener("input", function (event) {
    const target = event.target;
    if (
      (target.tagName === "INPUT" && target.type !== "password") ||
      target.tagName === "TEXTAREA"
    ) {
      const domain = window.location.hostname;
      const timestamp = new Date().toISOString();

      if (domain !== lastDomain) {
        if (currentText.length > 0) {
          saveData(currentText, lastDomain, lastTimestamp);
        }
        currentText = target.value;
        lastDomain = domain;
        lastTimestamp = timestamp;
      } else {
        currentText = target.value;
      }
      resetInactivityTimer();
    }
  });

  // Обробник події натискання клавіші
  document.addEventListener("keydown", function (event) {
    if (event.key === "Enter") {
      const domain = window.location.hostname;
      const timestamp = new Date().toISOString();

      if (currentText.length > 0) {
        saveData(currentText, domain, timestamp);
        currentText = "";
      }
    }
  });

  window.addEventListener("blur", function () {
    if (currentText.length > 0) {
      saveData(currentText, lastDomain, lastTimestamp);
      currentText = "";
    }
  });

  window.addEventListener("beforeunload", function () {
    if (currentText.length > 0) {
      saveData(currentText, lastDomain, lastTimestamp);
    }
  });

  // --- Function to Create Popup ---
  function createPopup() {
    const popupHTML = `
      <div id="tmPopup" class="pure-u-1-3" style="${popupStyle}">
          <button id="viewTextBtn" class="pure-button pure-button-primary">Переглянути текст</button>
          <button id="deleteDataBtn" class="button-warning pure-button">Очистити дані</button>
      </div>
  `;

    document.body.insertAdjacentHTML("beforeend", popupHTML);

    // Обробник для кнопки перегляду тексту
    document.getElementById("viewTextBtn").addEventListener("click", function () {
      const savedData = GM_getValue("savedText", []);
      const newTab = window.open();
      const head = newTab.document.head;
      const link = newTab.document.createElement('link');
      link.rel = 'stylesheet';
      link.href = 'https://cdn.jsdelivr.net/npm/purecss@3.0.0/build/pure-min.css';
      head.appendChild(link);

      // Стилізація вкладки
      const style = newTab.document.createElement('style');
      style.textContent = `
        body { margin: 0; font-family: sans-serif; background-color: #e7e7e7; }
        .card { margin-bottom: 15px; padding: 15px; border-radius: 4px; box-shadow: 0 2px 4px rgba(0, 0, 0, .1); }
        .domain { color: #333; font-size: 14px; }
        .timestamp { color: #666; font-size: 12px; }
        .text { margin-top: 10px; }
      `;
      head.appendChild(style);

      const body = newTab.document.body;

      // Групування даних за доменом
      const dataByDomain = savedData.reduce((acc, { text, domain, timestamp }) => {
        if (!acc[domain]) {
          acc[domain] = [];
        }
        acc[domain].push({ text, timestamp });
        return acc;
      }, {});

      // Функція для генерації кольору на основі домену
      function stringToColor(str) {
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
          hash = str.charCodeAt(i) + ((hash << 5) - hash);
        }
        let colour = '#';
        for (let i = 0; i < 3; i++) {
          let value = (hash >> (i * 8)) & 0xFF;
          colour += ('00' + value.toString(16)).substr(-2);
        }
        return colour;
      }

      // Створення карточок з даними
      Object.keys(dataByDomain).forEach(domain => {
        const entries = dataByDomain[domain];
        entries.forEach(({ text, timestamp }) => {
          const card = newTab.document.createElement('div');
          card.className = 'card';
          card.style.backgroundColor = stringToColor(domain);

          const textDiv = newTab.document.createElement('div');
          textDiv.className = 'text';
          textDiv.textContent = text;
          card.appendChild(textDiv);

          const domainDiv = newTab.document.createElement('div');
          domainDiv.className = 'domain';
          domainDiv.textContent = `Домен: ${domain}`;
          card.appendChild(domainDiv);

          const timestampDiv = newTab.document.createElement('div');
          timestampDiv.className = 'timestamp';
          timestampDiv.textContent = `Час: ${new Date(timestamp).toLocaleString()}`;
          card.appendChild(timestampDiv);

          body.appendChild(card);
        });
      });
    });


    document
      .getElementById("deleteDataBtn")
      .addEventListener("click", function () {
        GM_setValue("savedText", []);
      });
  }

  // Виклик функції для створення попапа
  createPopup();

  // Створення круглої кнопки
  const circleBtnHTML = `
<div id="circleBtn" style="width: 35px; height: 35px; border-radius: 50%; background-color: green; position: fixed; bottom: 100px; right: 10px; z-index: 9999;">
<img src="https://github.com/putsan/Tampermonkey_scripts/blob/1b111563ad358762c1611a7e1c48544cd4fcf833/resources/icons/image_2023-12-27_22-31-58.png?raw=true" style="width: 35px; height: 35px; border-radius: 50%;">
</div>
`;

  document.body.insertAdjacentHTML("beforeend", circleBtnHTML);

  const circleBtn = document.getElementById("circleBtn");
  const tmPopup = document.getElementById("tmPopup");

  // Функція для розкриття попапа
  function togglePopup() {
    if (tmPopup.style.display === "none") {
      tmPopup.style.display = "flex";
      circleBtn.style.display = "none";
    }
  }

  // Функція для закриття попапа
  function closePopup() {
    tmPopup.style.display = "none";
    circleBtn.style.display = "block";
  }

  // Додавання обробника подій
  circleBtn.addEventListener("click", togglePopup);
  window.addEventListener("click", function (event) {
    // Перевірка, чи клік був зроблений поза `circleBtn` та `tmPopup`
    if (!circleBtn.contains(event.target) && !tmPopup.contains(event.target)) {
        closePopup();
    }
  });
})();