General Calculator

Calculator that should work on every page :p

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

// ==UserScript==
// @name         General Calculator
// @namespace    https://greasyfork.org/en/users/1291009
// @version      2.0
// @description  Calculator that should work on every page :p
// @author       BadOrBest
// @license      MIT
// @icon         https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcSITQfCUO2ak5KcOYOskQl6nrLCyC7v73kkB0eIUyu5rsErnN45
// @match        *://*/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @run-at       document-end
// ==/UserScript==
(function() {
    'use strict';

    var chatBox = document.createElement('div');
    chatBox.id = 'calculator-chat-box';
    chatBox.style.position = 'fixed';
    chatBox.style.bottom = '10vh'; // Adjusted for mobile viewport
    chatBox.style.right = '5vw'; // Adjusted for mobile viewport
    chatBox.style.width = 'calc(90vw - 10px)'; // Adjusted for mobile viewport
    chatBox.style.maxWidth = '300px';
    chatBox.style.overflowY = 'hidden';
    chatBox.style.backgroundColor = '#222';
    chatBox.style.borderRadius = '20px';
    chatBox.style.padding = '0';
    chatBox.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
    chatBox.style.zIndex = '9999';
    chatBox.style.display = 'flex';
    chatBox.style.flexDirection = 'column';

// Draggable functionality
var isDragging = false;
var initialX, initialY;
var offsetX = 0;
var offsetY = 0;
var selectedForArrowKeys = false; // Added variable to track if chatBox is selected for arrow key dragging

chatBox.addEventListener('mousedown', startDragging);
chatBox.addEventListener('touchstart', startDragging);
chatBox.addEventListener('click', selectForArrowKeys); // Listen for click to select chatBox for arrow key dragging

// Listen for click events on the document to unselect the chatbox
document.addEventListener('click', function(event) {
    if (!chatBox.contains(event.target)) {
        selectedForArrowKeys = false; // Clicked outside of the chatbox, unselect it
    }
});

document.addEventListener('keydown', handleArrowKeys);

function startDragging(event) {
    if (event.target === chatInput) {
        return; // Don't start dragging if clicking on the input field
    }
    event.preventDefault();
    if (event.type === 'mousedown') {
        initialX = event.clientX - offsetX;
        initialY = event.clientY - offsetY;
    } else if (event.type === 'touchstart') {
        initialX = event.touches[0].clientX - offsetX;
        initialY = event.touches[0].clientY - offsetY;
    }
    isDragging = true;
    chatBox.classList.add('chat-dragging');

    document.addEventListener('mousemove', drag);
    document.addEventListener('touchmove', drag);

    document.addEventListener('mouseup', stopDragging);
    document.addEventListener('touchend', stopDragging);
}

function drag(event) {
    event.preventDefault();
    if (isDragging) {
        var currentX, currentY;
        if (event.type === 'mousemove') {
            currentX = event.clientX - initialX;
            currentY = event.clientY - initialY;
        } else if (event.type === 'touchmove') {
            currentX = event.touches[0].clientX - initialX;
            currentY = event.touches[0].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);
    document.removeEventListener('touchmove', drag);

    document.removeEventListener('mouseup', stopDragging);
    document.removeEventListener('touchend', stopDragging);
}

function handleArrowKeys(event) {
    if (!selectedForArrowKeys) {
        return; // If chatBox is not selected for arrow key dragging, exit function
    }
    var step = 10;
    switch (event.key) {
        case 'ArrowUp':
            offsetY -= step;
            break;
        case 'ArrowDown':
            offsetY += step;
            break;
        case 'ArrowLeft':
            offsetX -= step;
            break;
        case 'ArrowRight':
            offsetX += step;
            break;
    }
    chatBox.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
}

function selectForArrowKeys(event) {
    selectedForArrowKeys = true; // Set chatBox as selected for arrow key dragging
}


    // Title
    var chatTitle = document.createElement('div');
    chatTitle.style.background = '#444';
    chatTitle.style.color = 'white';
    chatTitle.style.padding = '10px';
    chatTitle.style.textAlign = 'center';
    chatTitle.style.fontWeight = 'bold';
    chatTitle.textContent = 'Calculator';
    chatBox.appendChild(chatTitle);

// Collapse Button
var collapseButton = document.createElement('button');
collapseButton.textContent = '▼';
collapseButton.style.position = 'absolute';
collapseButton.style.top = '0';
collapseButton.style.right = '0';
collapseButton.style.margin = '10px';
collapseButton.style.width = '30px';
collapseButton.style.height = '30px';
collapseButton.style.fontSize = '20px';
collapseButton.style.border = 'none';
collapseButton.style.backgroundColor = '#444';
collapseButton.style.color = 'white';
collapseButton.style.cursor = 'pointer';
collapseButton.style.borderRadius = '50%';

// Add click event listener
collapseButton.addEventListener('click', function(event) {
    toggleChatBox(event);
});

// Add touch event listener
collapseButton.addEventListener('touchstart', function(event) {
    toggleChatBox(event);
});

chatBox.appendChild(collapseButton);

// Toggle chat box visibility
function toggleChatBox(event) {
    // Stop propagation of the click event to prevent it from triggering dragging
    event.stopPropagation();

    if (chatBox.style.height === 'auto' || chatBox.style.height === '') {
        chatBox.style.height = '70px'; // Set to a fixed height
        collapseButton.textContent = '▲';
        chatInput.style.display = 'none'; // Hide the input field
    } else {
        chatBox.style.height = 'auto';
        collapseButton.textContent = '▼';
        chatInput.style.display = 'block'; // Show the input field
    }
}

    // Chat history container
    var chatHistory = document.createElement('div');
    chatHistory.id = 'chat-history';
    chatHistory.style.height = 'calc(50vh - 110px)';
    chatHistory.style.overflowY = 'auto';
    chatHistory.style.backgroundColor = '#333';
    chatBox.appendChild(chatHistory);

    // Function to add message to conversation
    function addMessage(message, isInput) {
        var messageElement = document.createElement('div');
        messageElement.className = 'message ' + (isInput ? 'input' : 'output');
        messageElement.innerHTML = message;
        chatHistory.appendChild(messageElement);

        if (!isInput) {
            var line = document.createElement('hr');
            line.style.borderTop = '1px solid white';
            line.style.margin = '5px 0';
            chatHistory.appendChild(line);
        }

        chatHistory.scrollTop = chatHistory.scrollHeight;
    }

    // Function to evaluate and display the result
    function evaluateExpression(expression) {
        try {
            // Replace 'x' with '*' for mathematical operations
            expression = expression.replace(/(x)/g, '*');
            // Handling algebraic expressions
            var result;
            if (/\b[A-Za-z]+\b/.test(expression)) {
                // If expression contains letters
                var match = expression.match(/\b[A-Za-z]+\b/);
                var letter = match[0];
                var equation = expression.replace(/\b[A-Za-z]+\b/g, '0'); // Replace letters with '0' for evaluation
                result = solveEquation(equation, letter);
            } else {
                // If expression is purely mathematical
                result = eval(expression);
            }
            addMessage('<span class="bot-label">(Computer):</span> ' + result, false);
        } catch (error) {
            addMessage('<span class="bot-label">(Computer):</span> Error: ' + error.message, false);
        }
    }

    // Function to solve algebraic equation
    function solveEquation(equation, letter) {
        // For simplicity, let's assume linear equations of the form 'ax = b'
        var sides = equation.split('=');
        var a = eval(sides[0]);
        var b = eval(sides[1]);
        var result = b / a;
        return letter + ' = ' + result;
    }

    // Function to send message
    function sendMessage() {
        var expression = chatInput.value.trim();
        if (expression !== '') {
            addMessage('<span class="user-label">(User):</span> ' + expression, true);
            evaluateExpression(expression);
            chatInput.value = '';
        }
    }

    // Input field
    var chatInput = document.createElement('input');
    chatInput.type = 'text';
    chatInput.placeholder = 'Type here...';
    chatInput.style.width = 'calc(100% - 20px)';
    chatInput.style.padding = '10px';
    chatInput.style.margin = '10px auto'; // Centered horizontally
    chatInput.style.borderRadius = '10px';
    chatInput.style.border = 'none';
    chatInput.style.backgroundColor = '#444';
    chatInput.style.color = 'white';
    chatInput.style.zIndex = '10000'; // Ensure input field is above other elements
    chatBox.appendChild(chatInput);

    // Listen for Enter key press to send message
    chatInput.addEventListener('keydown', function(event) {
        if (event.key === 'Enter') {
            event.preventDefault();
            sendMessage();
        }
    });

    // CSS style for user and bot messages
    var style = document.createElement('style');
    style.innerHTML = `
        .message {
            clear: both;
            padding: 5px 10px;
            color: white;
        }
        .input {
            text-align: left;
        }
        .bot-label {
            float: left;
            margin-left: 5px;
            background-color: blue;
            color: white;
            border-radius: 10px;
            padding: 3px 6px;
        }
        .user-label {
            float: left;
            margin-left: 5px;
            background-color: green;
            color: white;
            border-radius: 10px;
            padding: 3px 6px;
        }
        hr {
            border-top: 1px solid white;
            margin: 5px 0;
        }
        .chat-dragging * {
            /* Removed user-select: none; to enable touch interactions */
        }
    `;
    document.head.appendChild(style);

    // Append chatBox to body
    document.body.appendChild(chatBox);
})();


// Mathematical functions
Math.sinh = function(x) { return (Math.exp(x) - Math.exp(-x)) / 2; };
Math.cosh = function(x) { return (Math.exp(x) + Math.exp(-x)) / 2; };
Math.tanh = function(x) { return (Math.exp(x) - Math.exp(-x)) / (Math.exp(x) + Math.exp(-x)); };
Math.asinh = function(x) { return Math.log(x + Math.sqrt(x * x + 1)); };
Math.acosh = function(x) { return Math.log(x + Math.sqrt(x * x - 1)); };
Math.atanh = function(x) { return Math.log((1 + x) / (1 - x)) / 2; };

// Mathematical constants
var E = Math.E; // Euler's number
var PI = Math.PI; // Pi
var SQRT2 = Math.SQRT2; // Square root of 2
var SQRT1_2 = Math.SQRT1_2; // Square root of 1/2

// Additional mathematical operations
Math.factorial = function(n) {
    if (n === 0 || n === 1)
        return 1;
    for (var i = n - 1; i >= 1; i--) {
        n *= i;
    }
    return n;
};

Math.permutation = function(n, r) {
    return Math.factorial(n) / Math.factorial(n - r);
};

Math.combination = function(n, r) {
    return Math.factorial(n) / (Math.factorial(r) * Math.factorial(n - r));
};

Math.roundTo = function(value, decimalPlaces) {
    var multiplier = Math.pow(10, decimalPlaces);
    return Math.round(value * multiplier) / multiplier;
};

// Trigonometric functions
Math.degToRad = function(degrees) {
    return degrees * (Math.PI / 180);
};

Math.radToDeg = function(radians) {
    return radians * (180 / Math.PI);
};

// Logarithmic functions
Math.log10 = function(x) {
    return Math.log(x) / Math.log(10);
};

Math.log2 = function(x) {
    return Math.log(x) / Math.log(2);
};

// Exponential functions
Math.exp10 = function(x) {
    return Math.pow(10, x);
};

Math.exp2 = function(x) {
    return Math.pow(2, x);
};

// Additional mathematical functions
Math.abs = function(x) { return Math.abs(x); };
Math.ceil = function(x) { return Math.ceil(x); };
Math.floor = function(x) { return Math.floor(x); };
Math.max = function(...args) { return Math.max(...args); };
Math.min = function(...args) { return Math.min(...args); };
Math.pow = function(x, y) { return Math.pow(x, y); };
Math.sqrt = function(x) { return Math.sqrt(x); };
Math.sign = function(x) { return Math.sign(x); };
Math.randomInt = function(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; };
Math.clamp = function(x, min, max) { return Math.max(min, Math.min(max, x)); };
Math.hypot = function(...args) { return Math.hypot(...args); };

// Constants
var LN2 = Math.LN2; // Natural logarithm of 2
var LN10 = Math.LN10; // Natural logarithm of 10
var LOG2E = Math.LOG2E; // Base 2 logarithm of E
var LOG10E = Math.LOG10E; // Base 10 logarithm of E
var SQRT3 = Math.SQRT3; // Square root of 3

// Exponential constants
var EXP = Math.exp(1); // Euler's number

// Trigonometric constants
var DEG_PER_RAD = 180 / Math.PI; // Degrees per radian
var RAD_PER_DEG = Math.PI / 180; // Radians per degree
var TWO_PI = 2 * Math.PI; // 2 * Pi

// Logarithmic constants
var LN_2 = Math.LN2; // Natural logarithm of 2
var LN_10 = Math.LN10; // Natural logarithm of 10
var LOG2_E = Math.LOG2E; // Base 2 logarithm of E
var LOG10_E = Math.LOG10E; // Base 10 logarithm of E

// Exponential constants
var SQRT_2 = Math.SQRT2; // Square root of 2
var SQRT_1_2 = Math.SQRT1_2; // Square root of 1/2

// Additional mathematical operations
Math.lcm = function(a, b) { return (!a || !b) ? 0 : Math.abs((a * b) / Math.gcd(a, b)); };
Math.gcd = function(a, b) { return (!b) ? a : Math.gcd(b, a % b); };
Math.factorial = function(n) { return (n !== 1 && n !== 0) ? n * Math.factorial(n - 1) : 1; };
Math.toDegrees = function(radians) { return radians * DEG_PER_RAD; };
Math.toRadians = function(degrees) { return degrees * RAD_PER_DEG; };
Math.isPrime = function(n) {
    if (n <= 1) return false;
    if (n <= 3) return true;
    if (n % 2 === 0 || n % 3 === 0) return false;
    let i = 5;
    while (i * i <= n) {
        if (n % i === 0 || n % (i + 2) === 0) return false;
        i += 6;
    }
    return true;
};