init geek calc
Some checks failed
Deploy to GitHub Pages / build-and-deploy (push) Has been cancelled
Some checks failed
Deploy to GitHub Pages / build-and-deploy (push) Has been cancelled
This commit is contained in:
194
utils.js
Normal file
194
utils.js
Normal file
@@ -0,0 +1,194 @@
|
||||
// Utility functions module
|
||||
const Utils = (function() {
|
||||
// Format a number for display
|
||||
function formatNumber(num) {
|
||||
if (typeof num === 'number') {
|
||||
// Check if it's an integer or a simple decimal
|
||||
if (Number.isInteger(num)) {
|
||||
return num.toString();
|
||||
} else {
|
||||
// Format to avoid floating point precision issues
|
||||
return parseFloat(num.toFixed(10)).toString();
|
||||
}
|
||||
}
|
||||
return num.toString();
|
||||
}
|
||||
|
||||
// Check if a value is a number
|
||||
function isNumber(value) {
|
||||
return !isNaN(parseFloat(value)) && isFinite(value);
|
||||
}
|
||||
|
||||
// Parse a string expression safely
|
||||
function parseExpression(expr) {
|
||||
// Remove any whitespace
|
||||
expr = expr.replace(/\s/g, '');
|
||||
|
||||
// Check if the expression contains only allowed characters
|
||||
if (!/^[0-9+\-*/().%]+$/.test(expr)) {
|
||||
return null; // Invalid expression
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
// Calculate expression with error handling
|
||||
function calculateExpression(expr) {
|
||||
try {
|
||||
// For security, avoid using eval directly
|
||||
// Instead, use the Function constructor with validation
|
||||
const validatedExpr = parseExpression(expr);
|
||||
if (!validatedExpr) {
|
||||
return 'Error';
|
||||
}
|
||||
|
||||
// Replace % with proper calculation
|
||||
let processedExpr = validatedExpr;
|
||||
if (processedExpr.includes('%')) {
|
||||
// Handle percentage expressions - this is a simplified approach
|
||||
// In a real implementation, you'd want more sophisticated percentage handling
|
||||
processedExpr = processedExpr.replace(/(\d+(\.\d+)?)%/g, (match, number) => {
|
||||
return `*${number}/100`;
|
||||
});
|
||||
}
|
||||
|
||||
const result = new Function(`"use strict"; return (${processedExpr})`)();
|
||||
|
||||
if (isNaN(result) || !isFinite(result)) {
|
||||
return 'Error';
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
return 'Error';
|
||||
}
|
||||
}
|
||||
|
||||
// Get expression precedence for operator
|
||||
function getPrecedence(operator) {
|
||||
switch (operator) {
|
||||
case '+':
|
||||
case '-':
|
||||
return 1;
|
||||
case '*':
|
||||
case '/':
|
||||
return 2;
|
||||
case '^':
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if character is an operator
|
||||
function isOperator(char) {
|
||||
return ['+', '-', '*', '/', '^'].includes(char);
|
||||
}
|
||||
|
||||
// Convert infix expression to postfix (for more complex expression evaluation)
|
||||
function infixToPostfix(expression) {
|
||||
const output = [];
|
||||
const operators = [];
|
||||
|
||||
// Simple tokenization (in a real implementation, you'd want more robust parsing)
|
||||
const tokens = expression.match(/\d+(\.\d+)?|[+\-*/()]/g) || [];
|
||||
|
||||
for (const token of tokens) {
|
||||
if (isNumber(token)) {
|
||||
output.push(token);
|
||||
} else if (token === '(') {
|
||||
operators.push(token);
|
||||
} else if (token === ')') {
|
||||
while (operators.length && operators[operators.length - 1] !== '(') {
|
||||
output.push(operators.pop());
|
||||
}
|
||||
operators.pop(); // Remove the '('
|
||||
} else if (isOperator(token)) {
|
||||
while (
|
||||
operators.length &&
|
||||
operators[operators.length - 1] !== '(' &&
|
||||
getPrecedence(operators[operators.length - 1]) >= getPrecedence(token)
|
||||
) {
|
||||
output.push(operators.pop());
|
||||
}
|
||||
operators.push(token);
|
||||
}
|
||||
}
|
||||
|
||||
while (operators.length) {
|
||||
output.push(operators.pop());
|
||||
}
|
||||
|
||||
return output.join(' ');
|
||||
}
|
||||
|
||||
// Debounce function to limit execution frequency
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
// Serialize state for storage
|
||||
function serializeState(state) {
|
||||
return JSON.stringify(state, (key, value) => {
|
||||
// Handle dates properly
|
||||
if (value instanceof Date) {
|
||||
return { __type: 'Date', value: value.toISOString() };
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
// Deserialize state from storage
|
||||
function deserializeState(str) {
|
||||
return JSON.parse(str, (key, value) => {
|
||||
// Restore dates properly
|
||||
if (value && typeof value === 'object' && value.__type === 'Date') {
|
||||
return new Date(value.value);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
// Update page title dynamically
|
||||
function updatePageTitle(title) {
|
||||
document.title = title || 'Geek Calculator';
|
||||
}
|
||||
|
||||
// Check if running in offline mode
|
||||
function isOffline() {
|
||||
return !navigator.onLine;
|
||||
}
|
||||
|
||||
// Format file size for display
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
return {
|
||||
formatNumber,
|
||||
isNumber,
|
||||
parseExpression,
|
||||
calculateExpression,
|
||||
getPrecedence,
|
||||
isOperator,
|
||||
infixToPostfix,
|
||||
debounce,
|
||||
serializeState,
|
||||
deserializeState,
|
||||
updatePageTitle,
|
||||
isOffline,
|
||||
formatFileSize
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user