- // ==UserScript==
- // @name Translate Selection Sidebar and Tooltip
- // @namespace https://aveusaid.wordpress.com
- // @version 0.9082024
- // @description Translate selected text, show in a tooltip, add to a sidebar list, and store in local storage
- // @author Usaid Bin Khalid Khan
- // @match *://*/*
- // @grant none
- // @license MIT
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- // Add CSS for the tooltip, sidebar, buttons, and dark mode text
- const styleElement = document.createElement('style');
- styleElement.type = 'text/css';
- styleElement.innerHTML = `
- .translator-tooltip {
- font-weight: 700;
- color: #000000;
- position: absolute;
- z-index: 10000;
- padding: 8px 12px;
- max-width: 300px;
- border-radius: 0.3em;
- background-color: #ffffdb;
- border: 1px solid #ccc;
- box-shadow: 0 2px 4px rgba(0,0,0,0.2);
- text-align: center;
- font-size: 18px;
- line-height: 1.4;
- visibility: hidden;
- opacity: 0;
- transition: visibility 0s linear 300ms, opacity 300ms;
- }
-
- .translator-tooltip.visible {
- visibility: visible;
- opacity: 1;
- }
-
- .translator-sidebar {
- position: fixed;
- top: 0;
- right: 0;
- width: 280px;
- height: 100%;
- background-color: #f7f7f7;
- overflow-y: auto;
- border-left: 2px solid #3388CC;
- padding: 20px;
- z-index: 10000;
- font-size: 16px;
- overflow: auto;
- box-shadow: -2px 0 5px rgba(0,0,0,0.1);
- box-sizing: border-box;
- resize: horizontal;
- min-width: 200px;
- max-width: 500px;
- display: none;
- }
-
- .translator-sidebar > * {
- margin-bottom: 20px;
- }
-
- .translator-entry {
- display: flex;
- flex-direction: column;
- padding: 10px 0;
- margin-bottom: 10px;
- border-bottom: 1px solid #ccc;
- }
-
- .translator-entry span:first-child {
- margin-bottom: 6px;
- font-weight: bold;
- color: #333;
- }
-
- .translator-entry span:last-child {
- color: #666;
- }
-
- .close-sidebar {
- position: absolute;
- top: 10px;
- right: 10px;
- cursor: pointer;
- font-weight: bold;
- color: #555;
- font-size: 20px;
- background-color: transparent;
- border: none;
- z-index: 10001;
- }
-
- .attractive-text {
- display: block;
- font-size: 14px;
- font-style: italic;
- text-decoration: none;
- color: #ff6666;
- margin-top: 20px;
- text-align: center;
- }
-
- .attractive-text:hover {
- color: #ff3333;
- }
-
- .clear-button,
- .copy-all-button,
- .mode-toggle {
- cursor: pointer;
- padding: 10px 15px;
- background-color: #f5f5f5;
- border: 1px solid #ccc;
- border-radius: 5px;
- box-shadow: 0 2px 4px rgba(0,0,0,0.2);
- font-size: 16px;
- margin: 10px 0;
- width: calc(100% - 20px);
- box-sizing: border-box;
- }
-
- .clear-button {
- margin-top: 20px;
- }
-
- .mode-toggle {
- display: flex;
- align-items: center;
- margin-top: 20px;
- }
-
- .mode-toggle span {
- margin-right: 10px;
- }
-
- .close-sidebar {
- position: absolute;
- top: 10px;
- right: 10px;
- cursor: pointer;
- font-weight: bold;
- color: #555;
- font-size: 20px;
- background-color: transparent;
- border: none;
- z-index: 10001;
- }
-
- .attractive-text {
- display: block;
- font-size: 14px;
- font-style: italic;
- text-decoration: none;
- color: #ff6666;
- margin-top: 20px;
- text-align: center;
- }
-
- .attractive-text:hover {
- color: #ff3333;
- }
-
- .error-message {
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- padding: 20px;
- background-color: #f8d7da;
- color: #721c24;
- border: 1px solid #f5c6cb;
- border-radius: 5px;
- z-index: 10002;
- font-size: 18px;
- text-align: center;
- visibility: hidden;
- opacity: 0;
- transition: visibility 0s linear 300ms, opacity 300ms;
- }
-
- .error-message.visible {
- visibility: visible;
- opacity: 1;
- }
-
- .open-sidebar-button {
- position: fixed;
- bottom: 140px;
- right: 20px;
- z-index: 10001;
- cursor: pointer;
- padding: 10px 15px;
- background-color: #f5f5f5;
- border: 1px solid #ccc;
- border-radius: 5px;
- box-shadow: 0 2px 4px rgba(0,0,0,0.2);
- font-size: 16px;
- display: block;
- }
-
- .empty-sidebar {
- font-style: italic;
- color: grey;
- margin-top: 20px;
- text-align: center;
- }
-
- .dark-mode .translator-sidebar {
- background-color: #333333; /* Dark background color */
- color: #ffffff; /* White text */
- }
-
- .dark-mode .translator-entry span:first-child {
- color: #FFD700; /* Sunset yellow */
- }
-
- .dark-mode .translator-entry span:last-child {
- color: #ffffff; /* White */
- }
-
- .dark-mode .clear-button,
- .dark-mode .open-sidebar-button,
- .dark-mode .copy-all-button {
- background-color: #444;
- color: #FFD700; /* Sunset yellow */
- border-color: #666;
- }
- `;
- document.head.appendChild(styleElement);
-
- // Create tooltip element
- const tooltip = document.createElement('div');
- tooltip.className = 'translator-tooltip';
- document.body.appendChild(tooltip);
-
- // Create sidebar element
- const sidebar = document.createElement('div');
- sidebar.className = 'translator-sidebar resizable';
- document.body.appendChild(sidebar);
-
- // Create close button for sidebar
- const closeButton = document.createElement('button');
- closeButton.innerHTML = '×';
- closeButton.className = 'close-sidebar';
- closeButton.setAttribute('aria-label', 'Close sidebar');
- sidebar.appendChild(closeButton);
-
- // Create "Cogito, Ergo Sum" text
- const attractText = document.createElement('a');
- attractText.innerHTML = 'Cogito, Ergo Sum';
- attractText.href = 'https://aveusaid.wordpress.com';
- attractText.className = 'attractive-text';
- attractText.setAttribute('role', 'link');
- attractText.setAttribute('aria-label', 'Cogito, Ergo Sum');
- sidebar.appendChild(attractText);
-
- // Add two italic lines for empty sidebar
- const emptyLines = document.createElement('div');
- emptyLines.className = 'empty-sidebar';
- emptyLines.innerHTML = `
- <i>The Archives are empty.</i> <br>
- <i>Select some text to add it to the Translation Archives.</i>
- `;
- sidebar.appendChild(emptyLines);
-
- // Create clear button
- const clearButton = document.createElement('button');
- clearButton.textContent = 'Tabula Rasa';
- clearButton.className = 'clear-button';
- clearButton.setAttribute('aria-label', 'Clear translations');
- sidebar.appendChild(clearButton);
-
- // Create copy all button
- const copyAllButton = document.createElement('button');
- copyAllButton.textContent = 'Copy All';
- copyAllButton.className = 'copy-all-button';
- copyAllButton.setAttribute('aria-label', 'Copy all translations');
- sidebar.appendChild(copyAllButton);
-
- // Create mode toggle button
- const modeToggleButton = document.createElement('button');
- modeToggleButton.className = 'mode-toggle';
- modeToggleButton.textContent = 'Dim the Lights';
- sidebar.appendChild(modeToggleButton);
-
- // Create open sidebar button
- const openSidebarButton = document.createElement('button');
- openSidebarButton.textContent = 'The Archive';
- openSidebarButton.className = 'open-sidebar-button';
- openSidebarButton.setAttribute('aria-label', 'Open translation archive');
- document.body.appendChild(openSidebarButton);
-
- // Error message element
- const errorMessage = document.createElement('div');
- errorMessage.className = 'error-message';
- document.body.appendChild(errorMessage);
-
- let translations = [];
-
- function showError(message) {
- errorMessage.textContent = message;
- errorMessage.classList.add('visible');
- setTimeout(() => {
- errorMessage.classList.remove('visible');
- }, 3000);
- }
-
- function translateText(text) {
- return fetch(`https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=en&dt=t&q=${encodeURIComponent(text)}`)
- .then(response => response.json())
- .then(result => result[0][0][0])
- .catch(error => {
- showError('ERROR: The Network Towers Have Fallen!');
- console.error(error);
- });
- }
-
- function updateSidebar() {
- sidebar.innerHTML = '';
- sidebar.appendChild(closeButton);
- sidebar.appendChild(attractText);
- if (translations.length === 0) {
- sidebar.appendChild(emptyLines);
- } else {
- translations.forEach(({ original, translated }) => {
- const entry = document.createElement('div');
- entry.className = 'translator-entry';
- entry.innerHTML = `
- <span>${original}</span>
- <span>${translated}</span>
- `;
- sidebar.appendChild(entry);
- });
- }
- sidebar.appendChild(clearButton);
- sidebar.appendChild(copyAllButton);
- sidebar.appendChild(modeToggleButton);
- }
-
- function toggleSidebar() {
- if (sidebar.style.display === 'block') {
- sidebar.style.display = 'none';
- openSidebarButton.style.display = 'block'; // Show "The Archive" button
- } else {
- updateSidebar();
- sidebar.style.display = 'block';
- openSidebarButton.style.display = 'none'; // Hide "The Archive" button
- }
- }
-
- function clearTranslations() {
- translations = [];
- updateSidebar();
- }
-
- function copyAllTranslations() {
- const allTranslations = translations.map(({ original, translated }) => `${original}: ${translated}`).join('\n');
- navigator.clipboard.writeText(allTranslations).then(() => {
- showError('Translations copied to clipboard!');
- }).catch(() => {
- showError('Failed to copy translations.');
- });
- }
-
- function toggleDarkMode() {
- document.body.classList.toggle('dark-mode');
- const isDarkMode = document.body.classList.contains('dark-mode');
- modeToggleButton.textContent = isDarkMode ? 'Light the Way' : 'Dim the Lights';
- }
-
- document.addEventListener('mouseup', async (e) => {
- if (window.getSelection().toString()) {
- const selectedText = window.getSelection().toString();
- const translatedText = await translateText(selectedText);
- translations.push({ original: selectedText, translated: translatedText });
- updateSidebar();
- tooltip.textContent = translatedText;
- tooltip.style.left = `${e.pageX}px`;
- tooltip.style.top = `${e.pageY + 10}px`;
- tooltip.classList.add('visible');
- setTimeout(() => {
- tooltip.classList.remove('visible');
- }, 3000);
- }
- });
-
- openSidebarButton.addEventListener('click', toggleSidebar);
- closeButton.addEventListener('click', toggleSidebar);
- clearButton.addEventListener('click', clearTranslations);
- copyAllButton.addEventListener('click', copyAllTranslations);
- modeToggleButton.addEventListener('click', toggleDarkMode);
-
- })();