General Calculator

Calculator that should work on every page :p

当前为 2024-06-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name General Calculator
  3. // @namespace https://greasyfork.org/en/users/1291009
  4. // @version 2.2.1
  5. // @description Calculator that should work on every page :p
  6. // @author BadOrBest
  7. // @license MIT
  8. // @icon https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcSITQfCUO2ak5KcOYOskQl6nrLCyC7v73kkB0eIUyu5rsErnN45
  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. (function() {
  18. 'use strict';
  19.  
  20. var MAX_MESSAGES = 400; // Maximum number of messages to store
  21.  
  22. var chatBox = document.createElement('div');
  23. chatBox.id = 'calculator-chat-box';
  24. chatBox.style.position = 'fixed';
  25. chatBox.style.bottom = '10vh'; // Adjusted for mobile viewport
  26. chatBox.style.right = '5vw'; // Adjusted for mobile viewport
  27. chatBox.style.width = 'calc(90vw - 10px)'; // Adjusted for mobile viewport
  28. chatBox.style.maxWidth = '300px';
  29. chatBox.style.overflowY = 'hidden';
  30. chatBox.style.backgroundColor = '#222';
  31. chatBox.style.borderRadius = '20px';
  32. chatBox.style.padding = '0';
  33. chatBox.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
  34. chatBox.style.zIndex = '9999';
  35. chatBox.style.display = 'flex';
  36. chatBox.style.flexDirection = 'column';
  37.  
  38. // Draggable functionality
  39. var isDragging = false;
  40. var initialX, initialY;
  41. var offsetX = 0;
  42. var offsetY = 0;
  43. var selectedForArrowKeys = false; // Added variable to track if chatBox is selected for arrow key dragging
  44.  
  45. chatBox.addEventListener('mousedown', startDragging);
  46. chatBox.addEventListener('touchstart', startDragging);
  47. chatBox.addEventListener('click', selectForArrowKeys); // Listen for click to select chatBox for arrow key dragging
  48.  
  49. // Listen for click events on the document to unselect the chatbox
  50. document.addEventListener('click', function(event) {
  51. if (!chatBox.contains(event.target)) {
  52. selectedForArrowKeys = false; // Clicked outside of the chatbox, unselect it
  53. }
  54. });
  55.  
  56. document.addEventListener('keydown', handleArrowKeys);
  57.  
  58. function startDragging(event) {
  59. if (event.target === chatInput) {
  60. return; // Don't start dragging if clicking on the input field
  61. }
  62. event.preventDefault();
  63. if (event.type === 'mousedown') {
  64. initialX = event.clientX - offsetX;
  65. initialY = event.clientY - offsetY;
  66. } else if (event.type === 'touchstart') {
  67. initialX = event.touches[0].clientX - offsetX;
  68. initialY = event.touches[0].clientY - offsetY;
  69. }
  70. isDragging = true;
  71. chatBox.classList.add('chat-dragging');
  72.  
  73. document.addEventListener('mousemove', drag);
  74. document.addEventListener('touchmove', drag);
  75.  
  76. document.addEventListener('mouseup', stopDragging);
  77. document.addEventListener('touchend', stopDragging);
  78. }
  79.  
  80. function drag(event) {
  81. event.preventDefault();
  82. if (isDragging) {
  83. var currentX, currentY;
  84. if (event.type === 'mousemove') {
  85. currentX = event.clientX - initialX;
  86. currentY = event.clientY - initialY;
  87. } else if (event.type === 'touchmove') {
  88. currentX = event.touches[0].clientX - initialX;
  89. currentY = event.touches[0].clientY - initialY;
  90. }
  91.  
  92. offsetX = currentX;
  93. offsetY = currentY;
  94.  
  95. chatBox.style.transform = `translate(${currentX}px, ${currentY}px)`;
  96. }
  97. }
  98.  
  99. function stopDragging() {
  100. isDragging = false;
  101. chatBox.classList.remove('chat-dragging');
  102.  
  103. document.removeEventListener('mousemove', drag);
  104. document.removeEventListener('touchmove', drag);
  105.  
  106. document.removeEventListener('mouseup', stopDragging);
  107. document.removeEventListener('touchend', stopDragging);
  108. }
  109.  
  110. function handleArrowKeys(event) {
  111. if (!selectedForArrowKeys) {
  112. return; // If chatBox is not selected for arrow key dragging, exit function
  113. }
  114. var step = 10;
  115. switch (event.key) {
  116. case 'ArrowUp':
  117. offsetY -= step;
  118. break;
  119. case 'ArrowDown':
  120. offsetY += step;
  121. break;
  122. case 'ArrowLeft':
  123. offsetX -= step;
  124. break;
  125. case 'ArrowRight':
  126. offsetX += step;
  127. break;
  128. }
  129. chatBox.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
  130. }
  131.  
  132. function selectForArrowKeys(event) {
  133. selectedForArrowKeys = true; // Set chatBox as selected for arrow key dragging
  134. }
  135.  
  136. // Title
  137. var chatTitle = document.createElement('div');
  138. chatTitle.style.background = '#444';
  139. chatTitle.style.color = 'white';
  140. chatTitle.style.padding = '10px';
  141. chatTitle.style.textAlign = 'center';
  142. chatTitle.style.fontWeight = 'bold';
  143. chatTitle.textContent = 'Calculator';
  144. chatBox.appendChild(chatTitle);
  145.  
  146. // Collapse Button
  147. var collapseButton = document.createElement('button');
  148. collapseButton.textContent = '▼';
  149. collapseButton.style.position = 'absolute';
  150. collapseButton.style.top = '0';
  151. collapseButton.style.right = '0';
  152. collapseButton.style.margin = '10px';
  153. collapseButton.style.width = '30px';
  154. collapseButton.style.height = '30px';
  155. collapseButton.style.fontSize = '20px';
  156. collapseButton.style.border = 'none';
  157. collapseButton.style.backgroundColor = '#444';
  158. collapseButton.style.color = 'white';
  159. collapseButton.style.cursor = 'pointer';
  160. collapseButton.style.borderRadius = '50%';
  161.  
  162. // Add click event listener
  163. collapseButton.addEventListener('click', function(event) {
  164. toggleChatBox(event);
  165. });
  166.  
  167. // Add touch event listener
  168. collapseButton.addEventListener('touchstart', function(event) {
  169. toggleChatBox(event);
  170. });
  171.  
  172. chatBox.appendChild(collapseButton);
  173.  
  174. // Toggle chat box visibility
  175. function toggleChatBox(event) {
  176. // Stop propagation of the click event to prevent it from triggering dragging
  177. event.stopPropagation();
  178.  
  179. if (chatBox.style.height === 'auto' || chatBox.style.height === '') {
  180. chatBox.style.height = '70px'; // Set to a fixed height
  181. collapseButton.textContent = '▲';
  182. chatInput.style.display = 'none'; // Hide the input field
  183. } else {
  184. chatBox.style.height = 'auto';
  185. collapseButton.textContent = '▼';
  186. chatInput.style.display = 'block'; // Show the input field
  187. }
  188. }
  189.  
  190. // Chat history container
  191. var chatHistory = document.createElement('div');
  192. chatHistory.id = 'chat-history';
  193. chatHistory.style.height = 'calc(50vh - 110px)';
  194. chatHistory.style.overflowY = 'auto';
  195. chatHistory.style.backgroundColor = '#333';
  196. chatBox.appendChild(chatHistory);
  197.  
  198. // Function to load messages from localStorage
  199. function loadMessagesFromStorage() {
  200. var messages = [];
  201. try {
  202. messages = JSON.parse(localStorage.getItem('chatHistory')) || [];
  203. } catch (error) {
  204. console.error('Error loading chat history from localStorage:', error);
  205. // Clear localStorage if there's an error (e.g., quota exceeded)
  206. clearLocalStorage();
  207. }
  208. return messages;
  209. }
  210.  
  211. // Function to clear localStorage
  212. function clearLocalStorage() {
  213. try {
  214. localStorage.removeItem('chatHistory');
  215. } catch (error) {
  216. console.error('Error clearing localStorage:', error);
  217. }
  218. }
  219.  
  220. // Register menu commands using GM.registerMenuCommand
  221. GM.registerMenuCommand('Clear Chat History', clearLocalStorage);
  222.  
  223.  
  224. // Function to add message to conversation and save to localStorage
  225. function addMessage(message, isInput) {
  226. var messageElement = document.createElement('div');
  227. messageElement.className = 'message ' + (isInput ? 'input' : 'output');
  228. messageElement.innerHTML = message;
  229. chatHistory.appendChild(messageElement);
  230.  
  231. if (!isInput) {
  232. var line = document.createElement('hr');
  233. line.style.borderTop = '1px solid white';
  234. line.style.margin = '5px 0';
  235. chatHistory.appendChild(line);
  236. }
  237.  
  238. // Function to scroll to the bottom of the chat history
  239. function scrollToBottom() {
  240. chatHistory.scrollTop = chatHistory.scrollHeight;
  241. }
  242.  
  243. // Function to check and scroll to bottom if necessary
  244. function checkAndScrollToBottom() {
  245. // Check if user is already at the bottom or if there's a new message
  246. if (chatHistory.scrollTop + chatHistory.clientHeight === chatHistory.scrollHeight) {
  247. scrollToBottom();
  248. }
  249. }
  250. // Initial scroll to bottom when the page loads or chat initializes
  251. scrollToBottom();
  252.  
  253. // Save message to localStorage
  254. var messages = loadMessagesFromStorage();
  255. messages.push({ message: message, isInput: isInput });
  256.  
  257. // Limit messages to MAX_MESSAGES
  258. if (messages.length > MAX_MESSAGES) {
  259. messages = messages.slice(-MAX_MESSAGES);
  260. }
  261.  
  262. try {
  263. localStorage.setItem('chatHistory', JSON.stringify(messages));
  264. } catch (error) {
  265. console.error('Error saving chat history to localStorage:', error);
  266. // Clear localStorage if there's an error (e.g., quota exceeded)
  267. clearLocalStorage();
  268. }
  269. }
  270.  
  271. // Function to evaluate and display the result
  272. function evaluateExpression(expression) {
  273. try {
  274. // Replace 'x' with '*' for mathematical operations
  275. expression = expression.replace(/(x)/g, '*');
  276. // Handling algebraic expressions
  277. var result;
  278. if (/\b[A-Za-z]+\b/.test(expression)) {
  279. // If expression contains letters
  280. var match = expression.match(/\b[A-Za-z]+\b/);
  281. var letter = match[0];
  282. var equation = expression.replace(/\b[A-Za-z]+\b/g, '0'); // Replace letters with '0' for evaluation
  283. result = solveEquation(equation, letter);
  284. } else {
  285. // If expression is purely mathematical
  286. result = eval(expression);
  287. }
  288. addMessage('<span class="bot-label">(Calc):</span> ' + result, false);
  289. } catch (error) {
  290. addMessage('<span class="bot-label">(Calc):</span> Error: ' + error.message, false);
  291. }
  292. }
  293.  
  294. // Function to solve algebraic equation
  295. function solveEquation(equation, letter) {
  296. // For simplicity, let's assume linear equations of the form 'ax = b'
  297. var sides = equation.split('=');
  298. var a = eval(sides[0]);
  299. var b = eval(sides[1]);
  300. var result = b / a;
  301. return letter + ' = ' + result;
  302. }
  303.  
  304. // Function to send message
  305. function sendMessage() {
  306. var expression = chatInput.value.trim();
  307. if (expression !== '') {
  308. addMessage('<span class="user-label">(User):</span> ' + expression, true);
  309. evaluateExpression(expression);
  310. chatInput.value = '';
  311. }
  312. }
  313.  
  314. // Input field
  315. var chatInput = document.createElement('input');
  316. chatInput.type = 'text';
  317. chatInput.placeholder = 'Type here...';
  318. chatInput.style.width = 'calc(100% - 20px)';
  319. chatInput.style.padding = '10px';
  320. chatInput.style.margin = '10px auto'; // Centered horizontally
  321. chatInput.style.borderRadius = '10px';
  322. chatInput.style.border = 'none';
  323. chatInput.style.backgroundColor = '#444';
  324. chatInput.style.color = 'white';
  325. chatInput.style.zIndex = '10000'; // Ensure input field is above other elements
  326. chatBox.appendChild(chatInput);
  327.  
  328. // Listen for Enter key press to send message
  329. chatInput.addEventListener('keydown', function(event) {
  330. if (event.key === 'Enter') {
  331. event.preventDefault();
  332. sendMessage();
  333. }
  334. });
  335.  
  336. // CSS style for user and bot messages
  337. var style = document.createElement('style');
  338. style.innerHTML = `
  339. .message {
  340. clear: both;
  341. padding: 5px 10px;
  342. color: white;
  343. }
  344. .input {
  345. text-align: left;
  346. }
  347. .bot-label {
  348. float: left;
  349. margin-left: 5px;
  350. background-color: blue;
  351. color: white;
  352. border-radius: 10px;
  353. padding: 3px 6px;
  354. }
  355. .user-label {
  356. float: left;
  357. margin-left: 5px;
  358. background-color: green;
  359. color: white;
  360. border-radius: 10px;
  361. padding: 3px 6px;
  362. }
  363. hr {
  364. border-top: 1px solid white;
  365. margin: 5px 0;
  366. }
  367. .chat-dragging * {
  368. /* Removed user-select: none; to enable touch interactions */
  369. }
  370. `;
  371. document.head.appendChild(style);
  372.  
  373. // Append chatBox to body
  374. document.body.appendChild(chatBox);
  375.  
  376. // Load messages from localStorage on page load
  377. window.addEventListener('load', function() {
  378. var messages = loadMessagesFromStorage();
  379. messages.forEach(function(msg) {
  380. addMessage(msg.message, msg.isInput);
  381. });
  382. });
  383.  
  384. })();
  385.  
  386.  
  387. // Mathematical functions
  388. Math.sinh = function(x) { return (Math.exp(x) - Math.exp(-x)) / 2; };
  389. Math.cosh = function(x) { return (Math.exp(x) + Math.exp(-x)) / 2; };
  390. Math.tanh = function(x) { return (Math.exp(x) - Math.exp(-x)) / (Math.exp(x) + Math.exp(-x)); };
  391. Math.asinh = function(x) { return Math.log(x + Math.sqrt(x * x + 1)); };
  392. Math.acosh = function(x) { return Math.log(x + Math.sqrt(x * x - 1)); };
  393. Math.atanh = function(x) { return Math.log((1 + x) / (1 - x)) / 2; };
  394.  
  395. // Mathematical constants
  396. var E = Math.E; // Euler's number
  397. var PI = Math.PI; // Pi
  398. var SQRT2 = Math.SQRT2; // Square root of 2
  399. var SQRT1_2 = Math.SQRT1_2; // Square root of 1/2
  400.  
  401. // Additional mathematical operations
  402. Math.factorial = function(n) {
  403. if (n === 0 || n === 1)
  404. return 1;
  405. for (var i = n - 1; i >= 1; i--) {
  406. n *= i;
  407. }
  408. return n;
  409. };
  410.  
  411. Math.permutation = function(n, r) {
  412. return Math.factorial(n) / Math.factorial(n - r);
  413. };
  414.  
  415. Math.combination = function(n, r) {
  416. return Math.factorial(n) / (Math.factorial(r) * Math.factorial(n - r));
  417. };
  418.  
  419. Math.roundTo = function(value, decimalPlaces) {
  420. var multiplier = Math.pow(10, decimalPlaces);
  421. return Math.round(value * multiplier) / multiplier;
  422. };
  423.  
  424. // Trigonometric functions
  425. Math.degToRad = function(degrees) {
  426. return degrees * (Math.PI / 180);
  427. };
  428.  
  429. Math.radToDeg = function(radians) {
  430. return radians * (180 / Math.PI);
  431. };
  432.  
  433. // Logarithmic functions
  434. Math.log10 = function(x) {
  435. return Math.log(x) / Math.log(10);
  436. };
  437.  
  438. Math.log2 = function(x) {
  439. return Math.log(x) / Math.log(2);
  440. };
  441.  
  442. // Exponential functions
  443. Math.exp10 = function(x) {
  444. return Math.pow(10, x);
  445. };
  446.  
  447. Math.exp2 = function(x) {
  448. return Math.pow(2, x);
  449. };
  450.  
  451. // Additional mathematical functions
  452. Math.abs = function(x) { return Math.abs(x); };
  453. Math.ceil = function(x) { return Math.ceil(x); };
  454. Math.floor = function(x) { return Math.floor(x); };
  455. Math.max = function(...args) { return Math.max(...args); };
  456. Math.min = function(...args) { return Math.min(...args); };
  457. Math.pow = function(x, y) { return Math.pow(x, y); };
  458. Math.sqrt = function(x) { return Math.sqrt(x); };
  459. Math.sign = function(x) { return Math.sign(x); };
  460. Math.randomInt = function(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; };
  461. Math.clamp = function(x, min, max) { return Math.max(min, Math.min(max, x)); };
  462. Math.hypot = function(...args) { return Math.hypot(...args); };
  463.  
  464. // Constants
  465. var LN2 = Math.LN2; // Natural logarithm of 2
  466. var LN10 = Math.LN10; // Natural logarithm of 10
  467. var LOG2E = Math.LOG2E; // Base 2 logarithm of E
  468. var LOG10E = Math.LOG10E; // Base 10 logarithm of E
  469. var SQRT3 = Math.SQRT3; // Square root of 3
  470.  
  471. // Exponential constants
  472. var EXP = Math.exp(1); // Euler's number
  473.  
  474. // Trigonometric constants
  475. var DEG_PER_RAD = 180 / Math.PI; // Degrees per radian
  476. var RAD_PER_DEG = Math.PI / 180; // Radians per degree
  477. var TWO_PI = 2 * Math.PI; // 2 * Pi
  478.  
  479. // Logarithmic constants
  480. var LN_2 = Math.LN2; // Natural logarithm of 2
  481. var LN_10 = Math.LN10; // Natural logarithm of 10
  482. var LOG2_E = Math.LOG2E; // Base 2 logarithm of E
  483. var LOG10_E = Math.LOG10E; // Base 10 logarithm of E
  484.  
  485. // Exponential constants
  486. var SQRT_2 = Math.SQRT2; // Square root of 2
  487. var SQRT_1_2 = Math.SQRT1_2; // Square root of 1/2
  488.  
  489. // Additional mathematical operations
  490. Math.lcm = function(a, b) { return (!a || !b) ? 0 : Math.abs((a * b) / Math.gcd(a, b)); };
  491. Math.gcd = function(a, b) { return (!b) ? a : Math.gcd(b, a % b); };
  492. Math.factorial = function(n) { return (n !== 1 && n !== 0) ? n * Math.factorial(n - 1) : 1; };
  493. Math.toDegrees = function(radians) { return radians * DEG_PER_RAD; };
  494. Math.toRadians = function(degrees) { return degrees * RAD_PER_DEG; };
  495. Math.isPrime = function(n) {
  496. if (n <= 1) return false;
  497. if (n <= 3) return true;
  498. if (n % 2 === 0 || n % 3 === 0) return false;
  499. let i = 5;
  500. while (i * i <= n) {
  501. if (n % i === 0 || n % (i + 2) === 0) return false;
  502. i += 6;
  503. }
  504. return true;
  505. };