Advanced Calculator

A unified calculator tool with chat history and draggable UI.

当前为 2024-08-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Advanced Calculator
  3. // @namespace https://greasyfork.org/en/users/1291009
  4. // @version 6.1.0
  5. // @description A unified calculator tool with chat history and draggable UI.
  6. // @author BadOrBest
  7. // @license MIT
  8. // @icon https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQE4akrlePwM0brye6bimtz0ziOengL_C9rhQ&s
  9. // @match *://*/*
  10. // @grant GM_addStyle
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // @grant GM_deleteValue
  14. // @grant GM_registerMenuCommand
  15. // @run-at document-end
  16. // ==/UserScript==
  17.  
  18. (function() {
  19. 'use strict';
  20.  
  21. // Load external libraries
  22. const scriptLoad = url => new Promise((resolve, reject) => {
  23. const script = document.createElement('script');
  24. script.src = url;
  25. script.onload = resolve;
  26. script.onerror = reject;
  27. document.head.appendChild(script);
  28. });
  29.  
  30. Promise.all([
  31. scriptLoad('https://cdnjs.cloudflare.com/ajax/libs/mathjs/13.1.1/math.min.js'),
  32. scriptLoad('https://cdnjs.cloudflare.com/ajax/libs/localforage/1.10.0/localforage.min.js')
  33. ]).then(() => {
  34. console.log('Libraries loaded successfully.');
  35. initCalculator();
  36. }).catch(error => console.error('Error loading libraries:', error));
  37.  
  38. // General settings
  39. const settings = {
  40. theme: GM_getValue('theme', 'dark'),
  41. historyPageSize: GM_getValue('historyPageSize', 50),
  42. interpretXAsMultiply: GM_getValue('interpretXAsMultiply', true) // Default to true
  43. };
  44.  
  45. function applySettings() {
  46. document.body.style.backgroundColor = settings.theme === 'dark' ? '#333' : '#fff';
  47. }
  48.  
  49. applySettings();
  50.  
  51. // Calculator Initialization
  52. function initCalculator() {
  53. const MAX_MESSAGES = 400;
  54. const STORAGE_KEY = 'chatHistory';
  55.  
  56. const chatBox = document.createElement('div');
  57. chatBox.id = 'calculator-chat-box';
  58. Object.assign(chatBox.style, {
  59. position: 'fixed',
  60. bottom: '10vh',
  61. right: '5vw',
  62. width: 'calc(90vw - 10px)',
  63. maxWidth: '350px',
  64. backgroundColor: '#1f1f1f',
  65. borderRadius: '12px',
  66. padding: '10px',
  67. boxShadow: '0 0 15px rgba(0, 0, 0, 0.3)',
  68. zIndex: '9999',
  69. display: 'flex',
  70. flexDirection: 'column',
  71. fontFamily: 'Arial, sans-serif'
  72. });
  73.  
  74. const chatHeader = document.createElement('div');
  75. chatHeader.style.display = 'flex';
  76. chatHeader.style.justifyContent = 'space-between';
  77. chatHeader.style.alignItems = 'center';
  78. chatHeader.style.backgroundColor = '#333';
  79. chatHeader.style.color = '#fff';
  80. chatHeader.style.padding = '10px';
  81. chatHeader.style.borderRadius = '10px 10px 0 0';
  82. chatHeader.style.fontWeight = 'bold';
  83. chatHeader.textContent = 'Advanced Calculator';
  84.  
  85. const collapseButton = document.createElement('button');
  86. collapseButton.textContent = '▼';
  87. collapseButton.style.padding = '5px 10px';
  88. collapseButton.style.border = 'none';
  89. collapseButton.style.backgroundColor = '#555';
  90. collapseButton.style.color = '#fff';
  91. collapseButton.style.cursor = 'pointer';
  92. collapseButton.style.borderRadius = '5px';
  93. collapseButton.addEventListener('click', toggleChatBox);
  94.  
  95. chatHeader.appendChild(collapseButton);
  96. chatBox.appendChild(chatHeader);
  97.  
  98. const chatHistory = document.createElement('div');
  99. chatHistory.id = 'chat-history';
  100. Object.assign(chatHistory.style, {
  101. height: '300px',
  102. overflowY: 'auto',
  103. backgroundColor: '#333',
  104. color: '#fff',
  105. padding: '10px',
  106. borderRadius: '8px'
  107. });
  108. chatBox.appendChild(chatHistory);
  109.  
  110. const chatInput = document.createElement('input');
  111. chatInput.id = 'chat-input';
  112. chatInput.type = 'text';
  113. Object.assign(chatInput.style, {
  114. width: 'calc(100% - 20px)',
  115. padding: '10px',
  116. margin: '10px auto',
  117. borderRadius: '10px',
  118. border: 'none',
  119. backgroundColor: '#444',
  120. color: 'white'
  121. });
  122. chatInput.placeholder = 'Type here...';
  123. chatInput.addEventListener('keydown', event => {
  124. if (event.key === 'Enter') {
  125. event.preventDefault();
  126. sendMessage();
  127. }
  128. });
  129. chatBox.appendChild(chatInput);
  130.  
  131. // Draggable functionality
  132. let isDragging = false;
  133. let initialX, initialY;
  134. let offsetX = 0, offsetY = 0;
  135.  
  136. chatBox.addEventListener('mousedown', startDragging);
  137. chatBox.addEventListener('touchstart', startDragging);
  138.  
  139. document.addEventListener('mousemove', drag);
  140. document.addEventListener('touchmove', drag);
  141. document.addEventListener('mouseup', stopDragging);
  142. document.addEventListener('touchend', stopDragging);
  143.  
  144. function startDragging(event) {
  145. if (event.target === chatInput) return;
  146. event.preventDefault();
  147. initialX = event.clientX - offsetX;
  148. initialY = event.clientY - offsetY;
  149. isDragging = true;
  150. chatBox.classList.add('chat-dragging');
  151. }
  152.  
  153. function drag(event) {
  154. if (!isDragging) return;
  155. event.preventDefault();
  156. const currentX = event.clientX - initialX;
  157. const currentY = event.clientY - initialY;
  158. offsetX = currentX;
  159. offsetY = currentY;
  160. chatBox.style.transform = `translate(${currentX}px, ${currentY}px)`;
  161. }
  162.  
  163. function stopDragging() {
  164. isDragging = false;
  165. chatBox.classList.remove('chat-dragging');
  166. }
  167.  
  168. function toggleChatBox() {
  169. if (chatBox.style.height === '70px') {
  170. chatBox.style.height = 'auto';
  171. collapseButton.textContent = '▼';
  172. chatInput.style.display = 'block';
  173. } else {
  174. chatBox.style.height = '70px';
  175. collapseButton.textContent = '▲';
  176. chatInput.style.display = 'none';
  177. }
  178. }
  179.  
  180. // Function to load messages from localStorage
  181. function loadMessagesFromStorage() {
  182. let messages = [];
  183. try {
  184. messages = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
  185. } catch (error) {
  186. console.error('Error loading chat history from localStorage:', error);
  187. clearLocalStorage();
  188. }
  189. return messages;
  190. }
  191.  
  192. // Function to clear localStorage
  193. function clearLocalStorage() {
  194. try {
  195. localStorage.removeItem(STORAGE_KEY);
  196. } catch (error) {
  197. console.error('Error clearing localStorage:', error);
  198. }
  199. }
  200.  
  201. // Function to add message to conversation and save to localStorage
  202. function addMessage(message, isInput) {
  203. const messageElement = document.createElement('div');
  204. messageElement.className = 'message ' + (isInput ? 'input' : 'output');
  205. messageElement.innerHTML = message;
  206. chatHistory.appendChild(messageElement);
  207.  
  208. if (!isInput) {
  209. const line = document.createElement('hr');
  210. line.style.borderTop = '1px solid white';
  211. line.style.margin = '5px 0';
  212. chatHistory.appendChild(line);
  213. }
  214.  
  215. function scrollToBottom() {
  216. chatHistory.scrollTop = chatHistory.scrollHeight;
  217. }
  218.  
  219. scrollToBottom();
  220.  
  221. let messages = loadMessagesFromStorage();
  222. messages.push({ message: message, isInput: isInput });
  223.  
  224. if (messages.length > MAX_MESSAGES) {
  225. messages = messages.slice(-MAX_MESSAGES);
  226. }
  227.  
  228. try {
  229. localStorage.setItem(STORAGE_KEY, JSON.stringify(messages));
  230. } catch (error) {
  231. console.error('Error saving chat history to localStorage:', error);
  232. clearLocalStorage();
  233. }
  234. }
  235.  
  236. function sendMessage() {
  237. const expression = chatInput.value.trim();
  238. if (expression !== '') {
  239. addMessage('<span class="user-label">(User):</span> ' + expression, true);
  240. evaluateExpression(expression);
  241. chatInput.value = '';
  242. }
  243. }
  244.  
  245. function evaluateExpression(expression) {
  246. try {
  247. if (settings.interpretXAsMultiply) {
  248. expression = expression.replace(/(\d)x(\d)/g, '$1*$2');
  249. }
  250. const result = math.evaluate(expression);
  251. addMessage('<span class="result-label">(Result):</span> ' + result, false);
  252. } catch (error) {
  253. addMessage('<span class="result-label">(Result):</span> Error: ' + error.message, false);
  254. }
  255. }
  256.  
  257. window.addEventListener('load', () => {
  258. loadMessagesFromStorage().forEach(msg => {
  259. addMessage(msg.message, msg.isInput);
  260. });
  261. scrollToBottom();
  262. });
  263.  
  264. GM_registerMenuCommand('Clear Chat History', clearLocalStorage);
  265. GM_registerMenuCommand('Toggle x as multiply/variable', () => {
  266. settings.interpretXAsMultiply = !settings.interpretXAsMultiply;
  267. GM_setValue('interpretXAsMultiply', settings.interpretXAsMultiply);
  268. alert(`x is now interpreted as ${settings.interpretXAsMultiply ? 'multiplication' : 'variable'}.`);
  269. });
  270.  
  271. GM_addStyle(`
  272. #calculator-chat-box {
  273. font-family: Arial, sans-serif;
  274. box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
  275. }
  276. .message {
  277. clear: both;
  278. padding: 5px 10px;
  279. color: white;
  280. }
  281. .input {
  282. text-align: left;
  283. }
  284. .result-label {
  285. float: left;
  286. margin-left: 5px;
  287. background-color: blue;
  288. color: white;
  289. border-radius: 10px;
  290. padding: 3px 6px;
  291. }
  292. .user-label {
  293. float: left;
  294. margin-left: 5px;
  295. background-color: green;
  296. color: white;
  297. border-radius: 10px;
  298. padding: 3px 6px;
  299. }
  300. hr {
  301. border-top: 1px solid white;
  302. margin: 5px 0;
  303. }
  304. .chat-dragging {
  305. cursor: move;
  306. }
  307. `);
  308.  
  309. document.body.appendChild(chatBox);
  310. }
  311. })();