📊 Results
📋 Step-by-step Calculation
Enter values to see calculation steps
📐 Interactive Parallelogram Diagram
📚 Calculation History
No calculations yet. Start by entering some values!
`);
printWindow.document.close();
printWindow.focus();
setTimeout(() => {
printWindow.print();
printWindow.close();
}, 250);
}// Dark mode functionality
function toggleDarkMode() {
isDarkMode = !isDarkMode;
const calculator = document.getElementById('calculator');
if (isDarkMode) {
calculator.classList.add('dark-mode');
localStorage.setItem('darkMode', 'true');
} else {
calculator.classList.remove('dark-mode');
localStorage.setItem('darkMode', 'false');
}
}// High contrast mode
function toggleHighContrast() {
const calculator = document.getElementById('calculator');
const isHighContrast = document.getElementById('highContrast').checked;
if (isHighContrast) {
calculator.classList.add('high-contrast');
} else {
calculator.classList.remove('high-contrast');
}
}// Auto-detect system dark mode preference
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches && !localStorage.getItem('darkMode')) {
toggleDarkMode();
}// Listen for system theme changes
if (window.matchMedia) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (!localStorage.getItem('darkMode')) {
if (e.matches && !isDarkMode) {
toggleDarkMode();
} else if (!e.matches && isDarkMode) {
toggleDarkMode();
}
}
});
}// Performance optimization: debounce input events
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}// Apply debouncing to input validation
const debouncedValidateAndCalculate = debounce(validateAndCalculate, 300);// Replace direct oninput calls with debounced version for better performance
document.addEventListener('DOMContentLoaded', function() {
const inputs = ['base', 'height', 'side1', 'side2', 'angle'];
inputs.forEach(id => {
const element = document.getElementById(id);
if (element) {
element.addEventListener('input', debouncedValidateAndCalculate);
}
});
});// Add smooth scrolling for mobile navigation
function smoothScrollToSection(sectionId) {
const element = document.getElementById(sectionId);
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}// Touch gesture support for mobile devices
let touchStartX = 0;
let touchStartY = 0;document.addEventListener('touchstart', function(e) {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
});document.addEventListener('touchend', function(e) {
const touchEndX = e.changedTouches[0].clientX;
const touchEndY = e.changedTouches[0].clientY;
const deltaX = touchEndX - touchStartX;
const deltaY = touchEndY - touchStartY;// Detect swipe gestures (simplified)
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) {
if (deltaX > 0) {
// Swipe right - could switch to base-height method
if (currentMethod !== 'baseHeight') {
switchMethod('baseHeight');
}
} else {
// Swipe left - could switch to sides-angle method
if (currentMethod !== 'sidesAngle') {
switchMethod('sidesAngle');
}
}
}
});// Accessibility enhancements
function announceResult() {
const area = document.getElementById('areaResult').textContent;
const unit = document.getElementById('areaUnit').textContent;
const announcement = `Area calculated: ${area} ${unit}`;
// Create a live region for screen readers
const liveRegion = document.createElement('div');
liveRegion.setAttribute('aria-live', 'polite');
liveRegion.setAttribute('aria-atomic', 'true');
liveRegion.style.position = 'absolute';
liveRegion.style.left = '-10000px';
liveRegion.textContent = announcement;
document.body.appendChild(liveRegion);
setTimeout(() => document.body.removeChild(liveRegion), 1000);
}// Enhanced keyboard navigation
document.addEventListener('keydown', function(e) {
// Tab navigation enhancements
if (e.key === 'Tab') {
const focusableElements = document.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstFocusable = focusableElements[0];
const lastFocusable = focusableElements[focusableElements.length - 1];if (e.shiftKey) {
if (document.activeElement === firstFocusable) {
e.preventDefault();
lastFocusable.focus();
}
} else {
if (document.activeElement === lastFocusable) {
e.preventDefault();
firstFocusable.focus();
}
}
}
// Additional keyboard shortcuts
if (e.ctrlKey || e.metaKey) {
switch (e.key) {
case 'r':
e.preventDefault();
resetCalculator();
break;
case 'c':
e.preventDefault();
copyResults();
break;
case 'd':
e.preventDefault();
toggleDarkMode();
break;
}
}
});// Auto-save functionality (using memory storage)
let autoSaveData = {};function autoSave() {
const inputs = ['base', 'height', 'side1', 'side2', 'angle'];
inputs.forEach(id => {
const element = document.getElementById(id);
if (element && element.value) {
autoSaveData[id] = element.value;
}
});
// Save method and other settings
autoSaveData.currentMethod = currentMethod;
autoSaveData.precision = document.getElementById('precision').value;
autoSaveData.showPerimeter = document.getElementById('showPerimeter').checked;
}function loadAutoSave() {
if (Object.keys(autoSaveData).length === 0) return;
// Restore input values
Object.keys(autoSaveData).forEach(key => {
const element = document.getElementById(key);
if (element && typeof autoSaveData[key] === 'string') {
element.value = autoSaveData[key];
} else if (key === 'currentMethod') {
switchMethod(autoSaveData[key]);
} else if (key === 'showPerimeter') {
document.getElementById('showPerimeter').checked = autoSaveData[key];
}
});
updateCalculation();
}// Auto-save every 30 seconds
setInterval(autoSave, 30000);// Save on page unload
window.addEventListener('beforeunload', autoSave);// Error handling and recovery
window.addEventListener('error', function(e) {
console.error('Calculator error:', e.error);
// Try to recover by resetting to a known good state
setTimeout(() => {
try {
resetCalculator();
loadAutoSave();
} catch (recoveryError) {
console.error('Recovery failed:', recoveryError);
}
}, 100);
});// Performance monitoring (simplified)
function measurePerformance(name, fn) {
const start = performance.now();
const result = fn();
const end = performance.now();
console.log(`${name} took ${end - start} milliseconds`);
return result;
}// Lazy loading for heavy operations
function lazyLoadFeatures() {
// This would be where we'd load additional features on demand
// For now, just ensuring all features are ready
console.log('All calculator features loaded successfully');
}// Initialize lazy loading after page load
window.addEventListener('load', () => {
setTimeout(lazyLoadFeatures, 100);
});// Analytics (privacy-friendly, no external services)
function trackUsage(action, details = {}) {
// Simple usage tracking for improvement purposes
const usage = {
action,
details,
timestamp: Date.now(),
userAgent: navigator.userAgent.substring(0, 100) // Truncated for privacy
};
// Store in memory only (no external tracking)
if (!window.calculatorUsage) {
window.calculatorUsage = [];
}
window.calculatorUsage.push(usage);
// Keep only last 50 actions to prevent memory issues
if (window.calculatorUsage.length > 50) {
window.calculatorUsage = window.calculatorUsage.slice(-50);
}
}// Track key user actions
const originalSwitchMethod = switchMethod;
switchMethod = function(method) {
trackUsage('method_switch', { method });
return originalSwitchMethod.call(this, method);
};const originalUpdateCalculation = updateCalculation;
updateCalculation = function() {
trackUsage('calculation', { method: currentMethod });
return originalUpdateCalculation.call(this);
};// Final initialization
console.log('Parallelogram Area Calculator initialized successfully');
// Announce readiness to screen readers
setTimeout(() => {
const announcement = document.createElement('div');
announcement.setAttribute('aria-live', 'polite');
announcement.style.position = 'absolute';
announcement.style.left = '-10000px';
announcement.textContent = 'Parallelogram Area Calculator is ready for use';
document.body.appendChild(announcement);
setTimeout(() => document.body.removeChild(announcement), 2000);
}, 1000);