- // ==UserScript==
- // @name Advanced Calculator
- // @namespace https://gf.qytechs.cn/en/users/1291009
- // @version 6.1.5
- // @description A unified calculator tool with chat history and draggable UI.
- // @author BadOrBest
- // @license MIT
- // @icon https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQE4akrlePwM0brye6bimtz0ziOengL_C9rhQ&s
- // @match *://*/*
- // @grant GM_addStyle
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_deleteValue
- // @grant GM_registerMenuCommand
- // @run-at document-end
- // ==/UserScript==
- (function() {
- 'use strict';
-
- // Load external libraries
- const scriptLoad = url => new Promise((resolve, reject) => {
- const script = document.createElement('script');
- script.src = url;
- script.onload = resolve;
- script.onerror = reject;
- document.head.appendChild(script);
- });
-
- Promise.all([
- scriptLoad('https://cdnjs.cloudflare.com/ajax/libs/mathjs/13.1.1/math.min.js'),
- scriptLoad('https://cdnjs.cloudflare.com/ajax/libs/localforage/1.10.0/localforage.min.js')
- ]).then(() => {
- console.log('Libraries loaded successfully.');
- initCalculator();
- }).catch(error => console.error('Error loading libraries:', error));
-
- // General settings
- const settings = {
- theme: GM_getValue('theme', 'dark'),
- historyPageSize: GM_getValue('historyPageSize', 50),
- interpretXAsMultiply: GM_getValue('interpretXAsMultiply', true) // Default to true
- };
-
- // Calculator Initialization
- function initCalculator() {
- const MAX_MESSAGES = 400;
- const STORAGE_KEY = 'chatHistory';
- let lastResult = null; // Store the last result
-
- const chatBox = document.createElement('div');
- chatBox.id = 'calculator-chat-box';
- Object.assign(chatBox.style, {
- position: 'fixed',
- bottom: '10vh',
- right: '5vw',
- width: 'calc(90vw - 10px)',
- maxWidth: '350px',
- backgroundColor: settings.theme === 'dark' ? '#1f1f1f' : '#f9f9f9',
- borderRadius: '12px',
- padding: '10px',
- boxShadow: '0 0 15px rgba(0, 0, 0, 0.3)',
- zIndex: '9999',
- display: 'flex',
- flexDirection: 'column',
- fontFamily: 'Arial, sans-serif'
- });
-
- const chatHeader = document.createElement('div');
- chatHeader.style.display = 'flex';
- chatHeader.style.justifyContent = 'space-between';
- chatHeader.style.alignItems = 'center';
- chatHeader.style.backgroundColor = settings.theme === 'dark' ? '#333' : '#ddd';
- chatHeader.style.color = settings.theme === 'dark' ? '#fff' : '#333';
- chatHeader.style.padding = '10px';
- chatHeader.style.borderRadius = '10px 10px 0 0';
- chatHeader.style.fontWeight = 'bold';
- chatHeader.textContent = 'Advanced Calculator';
-
- const collapseButton = document.createElement('button');
- collapseButton.textContent = '▼';
- collapseButton.style.padding = '5px 10px';
- collapseButton.style.border = 'none';
- collapseButton.style.backgroundColor = '#555';
- collapseButton.style.color = '#fff';
- collapseButton.style.cursor = 'pointer';
- collapseButton.style.borderRadius = '5px';
- collapseButton.addEventListener('click', toggleChatBox);
-
- chatHeader.appendChild(collapseButton);
- chatBox.appendChild(chatHeader);
-
- const chatHistory = document.createElement('div');
- chatHistory.id = 'chat-history';
- Object.assign(chatHistory.style, {
- height: '300px',
- overflowY: 'auto',
- backgroundColor: settings.theme === 'dark' ? '#333' : '#fff',
- color: settings.theme === 'dark' ? '#fff' : '#333',
- padding: '10px',
- borderRadius: '8px'
- });
- chatBox.appendChild(chatHistory);
-
- const chatInput = document.createElement('input');
- chatInput.id = 'chat-input';
- chatInput.type = 'text';
- Object.assign(chatInput.style, {
- width: 'calc(100% - 20px)',
- padding: '10px',
- margin: '10px auto',
- borderRadius: '10px',
- border: 'none',
- backgroundColor: '#444',
- color: 'white'
- });
- chatInput.placeholder = 'Type here...';
- chatInput.addEventListener('keydown', event => {
- if (event.key === 'Enter') {
- event.preventDefault();
- if (chatInput.value.trim() === '') {
- if (lastResult !== null) {
- chatInput.value = lastResult; // Insert the last result into the input field
- }
- } else {
- sendMessage();
- }
- }
- });
- chatBox.appendChild(chatInput);
-
- // Draggable functionality
- let isDragging = false;
- let initialX, initialY;
- let offsetX = 0, offsetY = 0;
-
- chatBox.addEventListener('mousedown', startDragging);
- chatBox.addEventListener('touchstart', startDragging);
-
- document.addEventListener('mousemove', drag);
- document.addEventListener('touchmove', drag);
- document.addEventListener('mouseup', stopDragging);
- document.addEventListener('touchend', stopDragging);
-
- function startDragging(event) {
- if (event.target === chatInput) return;
- event.preventDefault();
- initialX = event.clientX - offsetX;
- initialY = event.clientY - offsetY;
- isDragging = true;
- chatBox.classList.add('chat-dragging');
- document.addEventListener('mousemove', drag); // Attach listeners only on start
- document.addEventListener('touchmove', drag);
- }
-
- function drag(event) {
- if (!isDragging) return;
- event.preventDefault();
- const currentX = event.clientX - initialX;
- const currentY = event.clientY - initialY;
- offsetX = currentX;
- offsetY = currentY;
- chatBox.style.transform = `translate(${currentX}px, ${currentY}px)`;
- }
-
- function stopDragging() {
- isDragging = false;
- chatBox.classList.remove('chat-dragging');
- document.removeEventListener('mousemove', drag); // Detach listeners on stop
- document.removeEventListener('touchmove', drag);
- }
-
- function toggleChatBox() {
- if (chatBox.style.height === '70px') {
- chatBox.style.height = 'auto';
- collapseButton.textContent = '▼';
- chatInput.style.display = 'block';
- } else {
- chatBox.style.height = '70px';
- collapseButton.textContent = '▲';
- chatInput.style.display = 'none';
- }
- }
-
- // Function to load messages from localStorage
- function loadMessagesFromStorage() {
- let messages = [];
- try {
- messages = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
- } catch (error) {
- console.error('Error loading chat history from localStorage:', error);
- clearLocalStorage();
- }
- return messages;
- }
-
- // Function to clear localStorage
- function clearLocalStorage() {
- try {
- localStorage.removeItem(STORAGE_KEY);
- } catch (error) {
- console.error('Error clearing localStorage:', error);
- }
- }
-
- // Function to add message to conversation and save to localStorage
- function addMessage(message, isInput) {
- const messageElement = document.createElement('div');
- messageElement.className = 'message ' + (isInput ? 'input' : 'output');
- messageElement.innerHTML = message;
- chatHistory.appendChild(messageElement);
-
- if (!isInput) {
- const line = document.createElement('hr');
- line.style.borderTop = '1px solid white';
- line.style.margin = '5px 0';
- chatHistory.appendChild(line);
- }
-
- function scrollToBottom() {
- chatHistory.scrollTop = chatHistory.scrollHeight;
- }
-
- scrollToBottom();
-
- let messages = loadMessagesFromStorage();
- messages.push({ message: message, isInput: isInput });
-
- if (messages.length > MAX_MESSAGES) {
- messages = messages.slice(-MAX_MESSAGES);
- }
-
- try {
- localStorage.setItem(STORAGE_KEY, JSON.stringify(messages));
- } catch (error) {
- if (error.name === 'QuotaExceededError') {
- console.error('LocalStorage is full, clearing oldest messages.');
- messages.shift(); // Remove the oldest message and try saving again
- localStorage.setItem(STORAGE_KEY, JSON.stringify(messages));
- } else {
- console.error('Error saving chat history:', error);
- clearLocalStorage();
- }
- }
- }
-
- function sendMessage() {
- const expression = chatInput.value.trim();
- if (expression !== '') {
- addMessage('<span class="user-label">(User):</span> ' + expression, true);
- evaluateExpression(expression);
- chatInput.value = '';
- }
- }
-
- function evaluateExpression(expression) {
- try {
- if (settings.interpretXAsMultiply) {
- expression = expression.replace(/(\d+)\s*x\s*(\d+)/gi, '$1*$2'); // Handles better
- }
- const result = math.evaluate(expression);
- addMessage('<span class="result-label">(Result):</span> ' + result, false);
- lastResult = result; // Store the result for reuse
- } catch (error) {
- addMessage('<span class="result-label">(Result):</span> Error: ' + error.message, false);
- }
- }
-
- window.addEventListener('load', () => {
- // Prevent duplicate loading of messages
- chatHistory.innerHTML = ''; // Clear existing content before loading new messages
- loadMessagesFromStorage().forEach(msg => {
- addMessage(msg.message, msg.isInput);
- });
- scrollToBottom();
- });
-
- GM_registerMenuCommand('Clear Chat History', clearLocalStorage);
- GM_registerMenuCommand('Toggle x as multiply/variable', () => {
- settings.interpretXAsMultiply = !settings.interpretXAsMultiply;
- GM_setValue('interpretXAsMultiply', settings.interpretXAsMultiply);
- alert(`x is now interpreted as ${settings.interpretXAsMultiply ? 'multiplication' : 'variable'}.`);
- });
-
- GM_addStyle(`
- #calculator-chat-box {
- font-family: Arial, sans-serif;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
- }
- #calculator-chat-box .message {
- clear: both;
- padding: 5px 10px;
- color: white;
- }
- #calculator-chat-box .input {
- text-align: left;
- }
- #calculator-chat-box .result-label {
- float: left;
- margin-left: 5px;
- background-color: #1a73e8; /* Improved contrast */
- color: white;
- border-radius: 10px;
- padding: 3px 6px;
- }
- #calculator-chat-box .user-label {
- float: left;
- margin-left: 5px;
- background-color: #34a853; /* Improved contrast */
- color: white;
- border-radius: 10px;
- padding: 3px 6px;
- }
- #calculator-chat-box hr {
- border-top: 1px solid white;
- margin: 5px 0;
- }
- #calculator-chat-box.chat-dragging {
- cursor: move;
- }
- `);
-
- document.body.appendChild(chatBox);
- }
- })();